接泛型三
20.6泛型方法
泛型方法是與特定類型相關的方法。除了常規參數,泛型方法還命名了在使用方法時需要提供的一群組類型參數。泛型方法可以在類、結構或介面聲明中聲明,而它們本身可以是泛型或者非泛型的。如果一個泛型方法在一個泛型型別聲明中被聲明,那麼方法體可以引用方法的型別參數和包含聲明的型別參數。
class-member-declaration:(類成員聲明:)…generic-method-declaration (泛型方法聲明)struct-member-declaration:(結構成員聲明:)…generic-method-declaration(泛型方法聲明)interface-member-declaration:(介面成員聲明:)…interface-generic-method-declaration(介面泛型方法聲明)泛型方法的聲明可通過在方法的名字之後放置型別參數列表而實現。generic-method-declaration:(泛型方法聲明:)generic-method-header method-body(泛型方法頭 方法體)generic-method-header:(泛型方法頭:)
attributes opt method-modifiers opt return-type member-name type-parameter-list(formal-parameter-list opt ) type-parameter-constraints-clause opt
(特性可選 方法修飾符可選 傳回型別 成員名 型別參數列表 (正式參數列表可選 )型別參數約束語句可選)
interface-generic-method-declaration:(介面泛型方法聲明:)
attributes opt new opt return-type identifier type-parameter-list
(formal-parameter-list opt) type-parameter-constraints-clauses opt ;
(特性可選 new可選 傳回型別 標識符 型別參數列表 (正式參數列表可選) 型別參數約束語句可選 ;
型別參數列表和型別參數約束語句與泛型型別聲明具有同樣的文法和功能。由型別參數列表聲明的型別參數範圍貫穿整個泛型方法聲明,它可以被用於形成包括傳回值、方法體和型別參數約束語句,但不包括特性。
方法的型別參數的名字不能與同一方法的常規參數名字相同。
下面的例子尋找數組中的第一個元素,如果滿足給定test委託則存在。泛型委派在§20.4種描述。
public delegate bool Test<T>(T item);public class Finder{public static T Find<T>(T[] items , Test<T> test){foreach(T item in items){if(test(item)) return item;}throw new InvalidOperationException(“Item not found”);}}
泛型方法不能被聲明為extern。所有其他修飾符在泛型方法上都是有效。
20.6.1泛型方法簽名
為了比較簽名的目的,任何型別參數約束都將被忽略,就像是型別參數的名字,但型別參數的個數也是相應的,就像是型別參數從左至右的元素位置。下面的例子展示了這條規則影響下的方法簽名。
class A{}class B {}interface IX{T F1<T>(T[] a , int i); //錯誤,因為傳回型別和型別參數名字無關緊要,void F1<U>(U[] a ,int i); //這兩個聲明都有相同的簽名void F2<T><int x>; //OK,型別參數的數量是簽名的一部分void F2(int x);void F3<T>(T t) where T: A // 錯誤,約束不在簽名考慮之列void F3<T>(T t) where T:B: }
泛型方法的重載採用一條與在泛型型別聲明(§20.1.8)中管理方法重載相似的規則,進行了進一步的約束。兩個使用相同名字和相同數量的類型實參的泛型方法聲明不能有封閉類型實參的列表的參數類型,當它們以同樣的順序以相同的簽名產生兩個方法,被應用到相同順序的兩個方法上時。由於這條規則約束將不會被考慮。例如
class X<T>{void F<U>(T t , U u){…} //錯誤,X<int>.F<int> 產生了具有相同簽名的兩個方法void F<U>(U u, T t){…} //}
20.6.2虛擬泛型方法
泛型方法可以使用abstract,virtual和override修飾符聲明。當匹配方法重寫和介面實現時,將使用與§20.6.1中描述規則相匹配的簽名。當泛型方法重寫一個在基類中聲明的泛型方法,或者實現基底介面中的方法,為每個型別參數給定的約束必須在兩個聲明中是相同的,在這裡方法型別參數將由原始位置從左至右而被標識。
abstract class Base
{public abstract T F<T,U>(T t, U u);public abstract T G<T>(T t) where T: IComparable;}class Derived:Base{public override X F<X,Y>(X x ,Y y){…} //OKpublic override T G<T>(T t){…} //錯誤}
F的重寫是正確的,因為型別參數名字允許不同。G的重寫是錯誤的,因為給定的型別參數約束(在這裡沒有約束)與被重寫的方法不匹配。
20.6.3調用泛型方法
泛型方法調用可以顯式指定類型實參列表,或者省略類型實參列表,而依靠類型推斷來確定類型實參。方法調用的確切編譯時間處理,包括泛型方法調用,在§20.9.5中進行了描述。當泛型方法不使用型別參數列表調用時,類型推斷將按§20.6.4中所描述的進行。
下面的例子展示在類型推斷和類型實參替代參數列表後,重載決策是如何發生的。
class Test{static void F<T>(int x , T y){Console.WriteLine(“One”);}static void F<T>(T x , long y){Console.WriteLine(“two”);}static void Main(){F<int>(5,324); //ok,列印“one”F<byte>(5,324); //ok, 列印“two”F<double>(5,324); //錯誤,模糊的F(5,324); //ok,列印“one”F(5,324L); //錯誤,模糊的 }}
20.6.4類型實參推斷
當不指定類型實參而調用泛型方法時,類型推斷(type inference)處理將試圖為調用推斷類型實參。類型推斷的存在可以讓調用泛型方法時,採用更方便的文法,並且可以避免程式員指定冗餘的類型資訊。例如,給定方法聲明
class Util{static Random rand = new Random();static public T Choose<T>(T first , T second){return (rand.Next(2) == 0)?first:second}}
不顯式指定類型實參而調用 Choose方法也是可以的。
int i = Util.Choose(5,123); //調用Choose<int>string s = Util.Choose(“foo”,”bar”);//調用Choose<string>
通過類型推斷,類型實參int和string 將由方法的實參確定。
類型推斷作為方法調用(§20.9.5)編譯時間處理的一部分而發生,並且在調用的重載決策之前發生。當在一個方法調用中指定特定的方法組時,類型實參不會作為方法調用的一部分而指定,類型推斷將被應用到方法組中的每個泛型方法。如果類型推斷成功,被推斷的類型實參將被用於確定後續重載決策的實參類型。如果重載決策選擇將要調用的泛型方法,被推斷的類型實參將被用作調用的實際類型實參。如果特定方法類型推斷失敗,這個方法將不參與重載決策。類型推斷失敗自身將不會產生編譯時間錯誤。但當重載決策沒能找到適用的方法時,將會導致編譯時間錯誤。
如果所提供的實參個數與方法的參數個數不同,推斷將立刻失敗。否則,類型推斷將為提供給方法的每個正式實參獨立地發生。假定這個實參的類型為A,對應參數為類型P。類型推斷將按下列步驟關聯類別型A和P而。
如果以下任何一條成立,將不能從實參推斷任何東西(但類型推斷是成功的)
- P和方法的任何型別參數無關[1]
- 實參是null字元
- 實參是一個匿名方法
- 實參是一個方法組
如果P是一個數群組類型,A是一個同秩(rank)的數群組類型,那麼使用A和P的元素類型相應地替換A和P,並重複這個步驟。
如果P是一個數群組類型,而A 是一個不同秩的數群組類型,那麼泛型方法的類型推斷失敗。
如果P是方法的型別參數,那麼對於這個實參的類型推斷成功,並且A是那個類型實參所推斷的類型。
否則,P必須是一個構造類型。如果對於出現在P中的每個方法型別參數MX ,恰好可以確定一個類型TX,使用每個TX替換每個MX ,這將產生一個類型,對於這個類型,A可以通過標準的隱式轉換而被轉換,那麼對於這個實參的類型推斷成功,對於每個MX,TX 就是推斷的類型。方法型別參數約束(如果有的話),因為類型推斷的原因將會被忽略。如果對於一個給定的MX 沒有TX存在或者多於一個TX存在 ,那麼泛型方法的類型推斷將會失敗(多於一個TX 存在的情形只可能發生在,P是一個泛型介面類型,並且A實現了介面的多個構造版本)。
如果所有的方法實參都通過先前的演算法進行了處理,那麼從實參而來的所有推斷都將被匯聚。這組推斷必須有如下的屬性。
方法的每個型別參數必須有一個為其推斷的類型實參。簡而言之,這組推斷必須是完整的([/b]complete[/b])[/b];
如果型別參數出現多於一次,那麼對那個型別參數的所有的推斷都必須推斷相同的類型實參。簡而言之,這組介面必須是一致的([/b]consistent[/b])[/b]。
如果能夠找到一組完整而一致的推斷類型實參,那麼對於一個給定的泛型方法和實參列表,類型推斷就可以說是成功的。
如果泛型方法使用參數數組(§10.5.1.4)聲明,那麼類型推斷針對方法將以其通常的方式執行。如果類型推斷成功,結果方法是可用的,那麼方法將以其通常形式對於重載決策是可行的。否則,類型推斷將針對方法的擴充形式(§7.4.2.1)執行。
[1]也就是P並不是方法的型別參數列表所列類型之一。
以上就是C# 2.0 Specification (泛型四)的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!