簡體   English   中英

具有繼承的模板類參數的集合

[英]Collection with inherited template class parameter

對我來說,這樣做是完美的:

public class A { }
public class B : A { }

public class C
{
    public List<A> b = new List<B>();
}

List期望元素屬於A類,List也是如此。 我知道類型不匹配,但是從邏輯上講,編譯器允許這種不匹配是很有意義的。 為什么不?

List<B>分配給List<A>

讓我們假設允許不匹配,並讓我們想象一下類C的以下虛構方法:

public void DoSomething()
{
    b.Add(new A()); // (1)
    List<B> tmp = (List<B>)b; // (2)
    foreach (B item in tmp) { // (3)
        // ...
    }
}
  • 第(1)行有效,因為b被鍵入為A項目的列表,因此自然地,我們可以向b添加新的A實例。
  • 第(2)行有效,因為b引用的實例實際上是List<B>的實例,因此強制轉換有效。
  • 第(3)行將崩潰,因為b包含的類型不是B tmp的類型為List<B>的事實保證了列表中的所有項目均為B類型,但是如果允許將List<B>分配給List<A>則情況不再如此。

因此,編譯器不允許這種不匹配。

這取決於通用容器是使用協變量還是 變量通用參數設置的。

您可以檢查MSDN以查看其聲明方式。

您期望List<T>T是協變的,但事實並非如此。

在C#中,某些接口是協變的,但類不是。 對於上面的AB ,說起來很合法

IEnumerable<A> b = new List<B>();

要么

IReadOnlyList<A> b = new List<B>();

因為所討論的接口是協變的,即用“ out”聲明為in

public interface IEnumerable<out T> ...

public interface IReadOnlyLies<out T> ...

因為如果列表存儲在List<A>變量中,則程序希望能夠將類型A對象放入該變量中。 List<B>對象存儲在List<A>變量中將使您無法將A類型A對象放置在明確聲明可以容納A類型A

那是:

List<A> b = new List<B>()
// compiler knows list should be of type A, so it expects this to work:
b.add(new A());

但是如果你可以指定一個List<B>List<A>變量,這將產生一個類型錯誤,即使編譯器知道變量b是類型的List<A> ,因此應該能夠保持一個類型A對象。

取而代之的是,您只使用new List<A>並向其添加類型B元素(允許這樣做),或者將變量的類型更改為List<B>

您不能使用類來實現。 您可以使用接口(查找協方差和逆方差),但不能使用List / IList 想象以下情況:

public class MyClass<T> 
{
    T GetT() { /* Blah blah */ }
    void SetT(T value) { /* Blah Blah */ }
}

寫這個:

MyClass<object> example = new MyClass<string>();

這將適用於第一種方法; example應返回一個object ,而MyClass<string>返回一個字符串,由於多態性,該字符串是合法的。

第二種方法更成問題。 假設您事后寫了這個:

example.SetT(new object());

這是非法的,因為MyClass<string>需要一個string但得到一個object 不好。

如前所述,您可以使用協方差和協方差對接口進行此工作。 您可以編寫這樣的接口:

public interface Covariant<out T>
{
    T FunctionReturningT();
}

public class MyInterfaceImplementation<T> : Covariant<T>
{
    public T FunctionReturningT() { /* Blah Blah */ }
}

使接口協變 ,這意味着寫是合法的:

Covariant<object> example = new MyInterfaceImplementation<string>();

而您也可以編寫以下內容:

public interface Contravariant<in T>
{
    void FunctionAskingForT(T value);
}

public class MyInterfaceImplementation<T> : Contravariant<T>
{
    public void FunctionAskingForT(T value) { /* Blah Blah */ }
}

您已經使該接口成為矛盾的 ,這意味着編寫此代碼是合法的:

Contravariant<string> example = new MyInterfaceImplementation<object>();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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