C# 2.0 Specification (泛型四)
2024-07-21 02:20:02
供稿:網友
 
接泛型三
這篇文章是翻譯的微軟的技術文章.供學習c#的朋友參考,請勿用于商業目的。http://msdn.microsoft.com/vcsharp/team/language/default.aspx
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){…} //ok
 public 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實現了接口的多個構造版本)。
如果所有的方法實參都通過先前的算法進行了處理,那么從實參而來的所有推斷都將被匯聚。這組推斷必須有如下的屬性。
方法的每個類型參數必須有一個為其推斷的類型實參。簡而言之,這組推斷必須是完整的(complete); 
如果類型參數出現多于一次,那么對那個類型參數的所有的推斷都必須推斷相同的類型實參。簡而言之,這組接口必須是一致的(consistent)。
 
如果能夠找到一組完整而一致的推斷類型實參,那么對于一個給定的泛型方法和實參列表,類型推斷就可以說是成功的。
如果泛型方法使用參數數組(§10.5.1.4)聲明,那么類型推斷針對方法將以其通常的方式執行。如果類型推斷成功,結果方法是可用的,那么方法將以其通常形式對于重載決策是可行的。否則,類型推斷將針對方法的擴展形式(§7.4.2.1)執行。
--------------------------------------------------------------------------------
[1]也就是p并不是方法的類型參數列表所列類型之一。