2009-11-04 6 views
0

나는 모든 IEnumerable<T> 확장 방법의 사용에 대해 약간 혼란 스럽다. intellisense는 항상 <T>을 요구하고 있지만, 항상 <T>을 지정할 필요는 없다고 생각한다.언제 IEnumerable 확장 메서드에 대해 <T> 형식을 지정해야합니까?

의 다음 I가 있다고 가정 해 봅시다 :

List<Person> people = GetSomePeople(); 

가 어떻게이 :

이에서
List<string> names = people.ConvertAll<string>(p=>p.Name).Distinct<string>().ToList<string>(); 

다른 :

List<string> names = people.ConvertAll<string>(p=>p.Name).Distinct().ToList(); 

나는 두 줄의 코드는 위의 sxactly 생각 같은, 지금 질문 :

<T>을 지정할시기와 건너 뛸시기를 어떻게 알 수 있습니까?

답변

4

가장 간단한 방법은 분명히 그것을 생략하고 그것이 컴파일되는지 확인하는 것입니다.

실제로 유형 매개 변수는 유추되는 곳마다 생략 할 수 있습니다. 일반적으로 사용자가 지정하는 것보다 메소드 매개 변수의 유형으로 사용될 때 유추 할 수 있습니다. 메서드의 반환 형식에 만 사용하면 유추 할 수 없습니다. 따라서 예를 들어 Enumerable.Select<T>의 경우 T은 첫 번째 인수 (유형이 IEnumerable<T>)의 유형에서 추론됩니다. 그러나 Enumerable.Empty<T>()에 대해서는 유추하지 않습니다. 왜냐하면 메서드의 반환 형식에서만 사용되었으므로 (아무 것도없는 것처럼) 어떤 인수에도 사용되지 않았기 때문입니다.

실제 규칙은 그보다 복잡하며 모든 인수를 추론 할 수있는 것은 아닙니다. 이 방법이 말 :

void Foo<T>(Func<T, T> x); 

을하고 람다로 전화를 시도 :

Foo(x => x); 

에도 T 불구하고 여기에 인수의 형태로 사용된다 유형을 추론 할 수있는 방법이 없습니다 - 이후 람다에는 타입 지정도 없습니다! 지금까지 컴파일러에 관한 한, T는 동일한 유형 x이, 그리고 x는 한편

이 작동합니다 ... 형 T이다 : 지금 충분한 유형이

Foo((int x) => x); 

이후 모든 것을 추론하는 정보. 또는 당신은 그것을 다른 방법으로 할 수있는 :

Foo<int>(x => x); 

추론에 대한 구체적인 단계별 규칙은 사실 상당히 복잡을, 당신은 여기에 기본 소스를 읽는 것이 가장 좋은 선택이 될 것입니다 - C# 언어 사양이다 .

1

이 기능을 유형 유추라고합니다. 예제에서 컴파일러는 ConvertAll에 대한 메서드 호출에서 매개 변수 lambda가 문자열 값 (예 : Name)을 반환하기 때문에 암시 적으로 일반 인자 유형을 자동으로 결정할 수 있습니다. 따라서 ConvertAll 호출의 일부분 인 <string>도 제거 할 수 있습니다.ConvertAll이 List<string>을 반환하고 컴파일러가 일반적인 인수를 선언 할 수 있기 때문에 Distict()에서도 마찬가지입니다.

답변으로, 컴파일러가 유형 자체를 결정할 수있을 때 일반적인 인수는 중복되고 불필요합니다. 대부분의 경우, 일반적인 인수를 전달해야하는 유일한 위치는 List<string> list = new List<string>();과 같은 선언입니다. 대신에 List<string>을 var로 대체하거나 람다에서 매개 변수로 템플릿을 사용할 때도 가능합니다.

+0

"암시 적 제네릭"이 아니라 실제로 "유형 유추"라고합니다. 그리고 타입 매개 변수가 아닌 제네릭 타입 * 인자 *를 추론합니다. 그냥 까다 롭습니다 :) –

+0

감사합니다. :) 편집 됨. 그리고 BTW, 내가 그렇게 정확하다면, 나는 명성으로 111k를 가졌을 것입니다. 그러나 슬프게도 ... 나는 또한 가끔 잠을 자고 오직 손으로 만 입력 할 수 있습니다. :피 – Yogesh

관련 문제