簡體   English   中英

“通用”類型參數中的類型參數/繼承

[英]“Generic” Type arguments / Inheritance in type arguments

假設我有一個功能

DoSomething(Dictionary<object, object> dict)

為什么不能被稱為

DoSomething(new Dictionary<int, string>())

由於intstring都繼承自object ,我希望這可以工作。 我知道我可以延伸到這個

DoSomething<T1, T2>(Dictionary<T1, T2> dict)

,但為什么第一個選項不起作用?

這是因為Dictionary<int, string>不是Dictionary<object, object>

要做到這一點,類類型Dictionary<TKey, TValue>需要在TKeyTValue協變,並且協方差需要使用值類型(裝箱)。

首先,在.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首次發表評論的原因)。 所以你沒有希望。

僅僅因為BA的子類型,並不意味着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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM