[英]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)
// ...
}
}
b
被鍵入為A
項目的列表,因此自然地,我們可以向b
添加新的A
實例。 b
引用的實例實際上是List<B>
的實例,因此強制轉換有效。 b
包含的類型不是B
tmp
的類型為List<B>
的事實保證了列表中的所有項目均為B
類型,但是如果允許將List<B>
分配給List<A>
則情況不再如此。 因此,編譯器不允許這種不匹配。
您期望List<T>
在T
是協變的,但事實並非如此。
在C#中,某些接口是協變的,但類不是。 對於上面的A
和B
,說起來很合法
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.