2009-11-04 95 views
0

我對使用所有的IEnumerable<T>擴展方法有點困惑,intellisense總是要求<T>,但我認爲有必要一直指定<T>什麼時候必須爲IEnumerable擴展方法指定類型<T>?

比方說,我有以下幾點:

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); 

,並嘗試用lambda來調用它:

Foo(x => x); 

即使T在類型參數在這裏被使用,有沒有辦法來推斷的類型 - 因爲lambda中沒有類型規範!至於編譯器而言,T是同一類型的x是,和xT型的...

在另一方面,這將工作:

Foo((int x) => x); 

,因爲現在有足夠的類型信息來推斷一切。或者你可以做的另一種方式:

Foo<int>(x => x); 

的具體一步一步的規則推論其實相當複雜,你最好離這裏閱讀的主要來源 - 這是C#語言規範。

1

此功能被稱爲類型推斷。在您的示例中,編譯器可以爲您自動確定泛型參數類型,因爲在調用ConvertAll的方法中,參數lambda返回字符串值(即Name)。所以你甚至可以刪除ConvertAll呼叫的<string>部分。與Distict()一樣,因爲ConvertAll返回List<string>,編譯器可以爲您聲明泛型參數。

至於你的回答,當編譯器可以確定類型本身時,泛型參數是多餘的和不必要的。大多數情況下,唯一需要傳遞泛型參數的地方是聲明,如List<string> list = new List<string>();。你可以用var代替第一個List<string>,或者當你在lambdas中使用模板作爲參數。

+0

它實際上被稱爲「類型推斷」而不是「隱式泛型」。它推斷泛型類型*參數*,而不是類型參數。只是要挑剔:) – 2009-11-04 06:24:46

+0

謝謝你的提升。 :)編輯。順便說一句,如果我非常準確,那麼我將擁有111k的聲望。但是,唉......我也有時會睡覺,只能用我的雙手打字。 :P – Yogesh 2009-11-04 06:28:40

相關問題