[英]“Generic” Type arguments / Inheritance in type arguments
假設我有一個功能
DoSomething(Dictionary<object, object> dict)
為什么不能被稱為
DoSomething(new Dictionary<int, string>())
?
由於int
和string
都繼承自object
,我希望這可以工作。 我知道我可以延伸到這個
DoSomething<T1, T2>(Dictionary<T1, T2> dict)
,但為什么第一個選項不起作用?
這是因為Dictionary<int, string>
不是Dictionary<object, object>
。
要做到這一點,類類型Dictionary<TKey, TValue>
需要在TKey
和TValue
協變,並且協方差需要使用值類型(裝箱)。
首先,在.NET中,泛型類類型總是不變的。 只有接口類型和委托類型可以是共變或逆變。
其次, Dictionary<,>
不是語義協變的。 例如你可以說:
myDict.Add(new Elephant(), new BankCustomer());
如果myDict
實際上是Dictionary<object, object>
變量中的(運行時) Dictionary<int, string>
,那就不會很愉快了。
現在,從.NET 4.5開始,一些“非字典”類型實現了協變接口,如IReadOnlyList<out T>
。 您可能希望類似的界面IReadOnlyDictionary<TKey, TValue>
也是協變的。 但事實並非如此(下面的Servy首次發表評論的原因)。 所以你沒有希望。
僅僅因為B
是A
的子類型,並不意味着List<B>
是List<A>
(或字典,無所謂)的子類型。
如果是,這在編譯時是合法的,但會導致運行時異常:
List<A> aList = new List<B>();
aList.Add(new C());
其中C是A的子類型。
為了使X<B>
成為X<A>
的子類型,則X<T>
必須在其類型參數T
是協變的。 為了使X
變為協變, T
必須僅用作其方法的返回類型,但絕不作為參數使用。
以IEnumerable<T>
為例 - 這是一個協變接口。 IEnumerable<string>
是IEnumerable<object>
的子類型,因為T
僅用作GetEnumerator
的返回類型。 因此,在編譯和運行時這樣做是合法的:
IEnumerable<object> enumerator = new List<string>();
foreach(object o in enumerator)
{
Console.WriteLine(o);
}
更多關於C#中的協方差和逆變: http : //blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx
因為Dictionary
對於兩個泛型參數的類型都是不變的。
在C#中, 所有泛型參數都是不變的。 通用參數只能是接口的協變或逆變。
對於任一類型參數, IDictionary
接口也不能是協變的或逆變的。 密鑰通過Keys
屬性傳遞,除了傳入...全部,並且除了傳遞出來之外,還使用Add
傳遞值...全部。 因為這兩種類型都用作輸入和輸出,所以類型必須是不變的。
那,協方差/逆變不適用於價值類型; 它只能用於引用類型。
只是提供一個簡單的例子,如果你被允許進行這樣的演員,那么我就可以在Dictionary<object, object>
上調用dict.Add("not an int", new Foo())
。 我現在添加了一個不是int的鍵和一個不是字符串的值。 因為允許使用強制轉換將允許使用與簽名不匹配的類型,所以它不起作用。
您可以使用它,因為類型不是這樣的協變或逆變。 舉個例子:
var dict=new Dictionary<int,string>();
DoSomething(dict);
DoSomething(Dictionary<object, object> dict)
{
var now=DateTime.Now;
dict[now]=now;
}
在您的場景中,您現在已將DateTime
添加到[int,string]的字典中!
僅僅因為Dictionary<T,Q>
不是協變的。
如果可能,則以下行將拋出運行時異常;
Dictionary<object, object> dict = new Dictionary<string, string>();
dict["hello"] = new SomeOtherObject();
由於在構建期間不會收到警告,因此最終會出現間歇性錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.