익명 형식을 매개 변수로 전달하는 방법은 무엇입니까?


143

익명 유형을 매개 변수로 다른 함수에 전달하려면 어떻게해야합니까? 이 예제를 고려하십시오.

var query = from employee in employees select new { Name = employee.Name, Id = employee.Id };
LogEmployees(query);

여기서 변수 query는 강력한 유형이 없습니다. LogEmployees함수를 받아들이려면 어떻게 함수를 정의해야 합니까?

public void LogEmployees (? list)
{
    foreach (? item in list)
    {

    }
}

즉, ?마크 대신에 무엇을 사용해야합니까 ?


1
데이터를 반환하지 않고 전달 매개 변수를 처리하는 더 나은 다른 중복 질문 : stackoverflow.com/questions/16823658/…
Rob Church

답변:


183

이 익명 유형에 대한 수업을 만들어야한다고 생각합니다. 그것은 제 생각에 가장 현명한 일입니다. 그러나 정말로 원하지 않는다면 역학을 사용할 수 있습니다.

public void LogEmployees (IEnumerable<dynamic> list)
{
    foreach (dynamic item in list)
    {
        string name = item.Name;
        int id = item.Id;
    }
}

이 유형 은 강력하게 입력 되지 않으므로 예를 들어 이름이 EmployeeName으로 변경되면 런타임까지 문제가 있음을 알 수 없습니다.


dynamic사용법 때문에 이것을 정답으로 확인했습니다 . 나는 정말로 나를 위해 편리했다. 감사합니다 :)
Saeed Neamati

1
일단 데이터가 전달되기 시작하면 버그를 찾기 어렵게하기 위해 더 체계적인 방법이 바람직 할 것입니다. 그러나 타협점을 찾으려면 또 다른 방법은 일반 사전을 전달하는 것입니다. C # 사전 초기화 프로그램은 요즘 사용하기에 매우 편리합니다.
조나스

일반적인 구현을 원하는 경우가 있으며 하드 타입을 전달한다는 것은 코드를 부풀리기 시작하는 스위칭 또는 팩토리 구현을 의미합니다. 당신이 진정으로 역동적 인 상황에 있고 당신이 받고있는 데이터를 다루기 위해 약간의 성찰을 신경 쓰지 않는다면, 이것은 완벽합니다. 답변 @Tim S.에 감사합니다
Larry Smith

42

다음과 같이 할 수 있습니다 :

public void LogEmployees<T>(List<T> list) // Or IEnumerable<T> list
{
    foreach (T item in list)
    {

    }
}

...하지만 각 항목별로 많은 것을 할 수는 없습니다. 당신은 ToString을 호출 할 수 있지만 (예를 들어)를 사용할 수 없습니다 NameId직접.


2
where T : some type첫 번째 줄 끝에서 사용 하여 유형을 좁힐 수 있습니다. 그 시점에서 특정 유형의 공통 인터페이스를 기대하면 인터페이스를 기대하는 것이 더 합리적입니다. :)
CassOnMars

9
@d_r_w : where T : some type어떤 종류의 인터페이스도 구현하지 않기 때문에 익명 타입과 함께 사용할 수 없습니다 .
Jon Skeet

@ dlev : 그렇게 할 수는 없습니다. foreach는 변수가 GetEnumerator를 구현할 때 반복되어야하며 익명 유형은이를 보장하지 않습니다.
CassOnMars

1
@ Jon Skeet : 좋은 지적입니다. 오늘 아침 뇌에 힘이 부족합니다.
CassOnMars

1
@JonSkeet. T가 익명 유형 인 경우 리플렉션을 사용하여 속성에 여전히 액세스하거나 설정할 수 있다고 가정합니까? 누군가가 "Select * from"문을 작성하고 익명 (또는 정의 된) 클래스를 사용하여 쿼리 결과의 열을 익명 개체의 동일한 명명 된 속성에 매핑하는 경우를 생각하고 있습니다.
C. Tewalt

19

불행히도, 당신이하려는 것은 불가능합니다. 후드 아래에서 쿼리 변수는IEnumerable 익명 형식으로 입력됩니다. 익명 형식 이름은 사용자 코드로 표현할 수 없으므로 함수에 입력 매개 변수를 만들 방법이 없습니다.

최선의 방법은 유형을 작성하고이를 조회의 리턴으로 사용하여 함수에 전달하는 것입니다. 예를 들어

struct Data {
  public string ColumnName; 
}

var query = (from name in some.Table
            select new Data { ColumnName = name });
MethodOp(query);
...
MethodOp(IEnumerable<Data> enumerable);

이 경우 단일 필드 만 선택하므로 필드를 직접 선택하는 것이 더 쉬울 수 있습니다. 그러면 IEnumerable필드 유형 으로 조회가 입력됩니다 . 이 경우 열 이름입니다.

var query = (from name in some.Table select name);  // IEnumerable<string>

내 예는 하나 였지만 대부분 더 컸습니다. 작품을 통한 당신의 대답 (그리고 지금은 분명합니다). 나는 그것을 생각하기 위해 점심을위한 휴식이 필요했다 ;-)
Tony Trembath-Drake


주의 할 점은 적절한 수업 Equals변경 행동 을 만들 때 입니다. 즉, 구현해야합니다. (이 불일치에 대해서는 알고 있었지만 리팩토링 중에는 여전히 잊어 버렸습니다.)
LosManos

11

매개 변수 유형이이 아닌 경우 익명 유형을 일반이 아닌 함수에 전달할 수 없습니다 object.

public void LogEmployees (object obj)
{
    var list = obj as IEnumerable(); 
    if (list == null)
       return;

    foreach (var item in list)
    {

    }
}

익명 유형은 메소드 내에서 단기 사용을위한 것입니다.

MSDN에서- 익명 유형 :

필드, 속성, 이벤트 또는 메서드의 반환 형식을 익명 형식으로 선언 할 수 없습니다. 마찬가지로 메서드, 속성, 생성자 또는 인덱서의 형식 매개 변수를 익명 형식으로 선언 할 수 없습니다. 익명 형식 또는 익명 형식이 포함 된 컬렉션을 메서드의 인수로 전달하려면 매개 변수를 type object로 선언하면됩니다 . 그러나 이렇게하면 강력한 타이핑의 목적이 무효가됩니다.

(강조 광산)


최신 정보

제네릭을 사용하여 원하는 것을 얻을 수 있습니다.

public void LogEmployees<T>(IEnumerable<T> list)
{
    foreach (T item in list)
    {

    }
}

4
익명 형식 (또는 익명 형식의 컬렉션)을 메서드에 전달할 수 없으면 LINQ 전체가 실패합니다. 익명 유형의 속성을 사용하지 않고 메서드가 완전히 제네릭되어야합니다.
Jon Skeet

2
re- object또는 dynamic; p
Marc Gravell

"as"로 캐스팅하는 경우 list가 null인지 확인해야합니다.
Alex

"can"! = "해야한다". 사용하는 object것은 내 대답에 따라 익명 유형의 일반 메소드를 만드는 것과 다릅니다.
Jon Skeet

8

일반적으로 다음과 같이 제네릭을 사용하여이 작업을 수행합니다.

MapEntToObj<T>(IQueryable<T> query) {...}

그런 다음 컴파일러는 T호출 할 때 를 유추해야합니다 MapEntToObj(query). 메소드 내부에서 무엇을하고 싶은지 잘 모르겠으므로 이것이 유용한 지 여부를 알 수 없습니다 ... 문제는 MapEntToObj여전히 내부에서 이름을 지정할 수 없다는 T것입니다.

  • 다른 일반적인 메소드를 호출 T
  • 에 반사를 사용 T하여 일을

그러나 그 외에는 익명 유형을 조작하는 것이 매우 어렵습니다.

데이터 를 추출 할 때의 또 다른 트릭 은 선택기를 전달하는 것입니다.

Foo<TSource, TValue>(IEnumerable<TSource> source,
        Func<TSource,string> name) {
    foreach(TSource item in source) Console.WriteLine(name(item));
}
...
Foo(query, x=>x.Title);

1
새로운 것을 배우고 익명의 유형이 변경 불가능하다는 것을 몰랐습니다! ;)
Annie Lagang

1
컴파일러가 생성하는 @AnneLagang은 컴파일러에 달려 있습니다. VB.NET에서 anon 유형은 변경 가능합니다.
Marc Gravell


7

다음 트릭으로 익명을 사용할 수 있습니다 (익명 유형으로 캐스트).

public void LogEmployees<T>(IEnumerable<T> list)
{
    foreach (T item in list)
    {
        var typedItem = Cast(item, new { Name = "", Id = 0 });
        // now you can use typedItem.Name, etc.
    }
}

static T Cast<T>(object obj, T type)
{
    return (T)obj;
}

6

"동적"도이 목적으로 사용될 수 있습니다.

var anonymousType = new { Id = 1, Name = "A" };

var anonymousTypes = new[] { new { Id = 1, Name = "A" }, new { Id = 2, Name = "B" };

private void DisplayAnonymousType(dynamic anonymousType)
{
}

private void DisplayAnonymousTypes(IEnumerable<dynamic> anonymousTypes)
{
   foreach (var info in anonymousTypes)
   {

   }
}

1
이것이 정답입니다! 그것은 단지 더 많은 사랑이 필요합니다 :)
Korayem

2

익명 유형을 전달하는 대신 동적 유형의 목록을 전달하십시오.

  1. var dynamicResult = anonymousQueryResult.ToList<dynamic>();
  2. 메소드 서명 : DoSomething(List<dynamic> _dynamicResult)
  3. 전화 방법 : DoSomething(dynamicResult);
  4. 끝난.

Petar Ivanov 에게 감사합니다 !


0

알고있는 경우 결과가 특정 인터페이스를 구현하면 인터페이스를 데이터 유형으로 사용할 수 있습니다.

public void LogEmployees<T>(IEnumerable<T> list)
{
    foreach (T item in list)
    {

    }
}

0

내가 사용하는 것이 IEnumerable<object>인수에 대한 유형으로. 그러나 피할 수없는 명백한 캐스트에는 큰 이익이 아닙니다. 건배

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.