[英]Please help me understand polymorphism when using generics in c#
我在使用泛型時理解多態如何工作時遇到了問題。 舉個例子,我定義了以下程序:
public interface IMyInterface
{
void MyMethod();
}
public class MyClass : IMyInterface
{
public void MyMethod()
{
}
}
public class MyContainer<T> where T : IMyInterface
{
public IList<T> Contents;
}
我可以這樣做,這很好用:
MyContainer<MyClass> container = new MyContainer<MyClass>();
container.Contents.Add(new MyClass());
我有很多實現MyInterface的類。 我想編寫一個可以接受所有MyContainer對象的方法:
public void CallAllMethodsInContainer(MyContainer<IMyInterface> container)
{
foreach (IMyInterface myClass in container.Contents)
{
myClass.MyMethod();
}
}
現在,我想稱之為這種方法。
MyContainer<MyClass> container = new MyContainer<MyClass>();
container.Contents.Add(new MyClass());
this.CallAllMethodsInContainer(container);
那沒用。 當然,因為MyClass實現了IMyInterface,我應該能夠實現它嗎?
MyContainer<IMyInterface> newContainer = (MyContainer<IMyInterface>)container;
那也行不通。 我絕對可以將一個普通的MyClass轉換為IMyInterface:
MyClass newClass = new MyClass();
IMyInterface myInterface = (IMyInterface)newClass;
所以,至少我沒有完全誤解這一點。 我不確定我是如何編寫一個接受符合相同接口的類的泛型集合的方法。
如果需要的話,我有計划徹底解決這個問題,但我真的更願意這樣做。
先感謝您。
注意:在所有情況下,您必須將Contents
字段初始化為實現IList<?>
的具體對象
保留泛型約束時,可以執行以下操作:
public IList<T> Contents = new List<T>();
如果不這樣做,您可以:
public IList<MyInterface> Contents = new List<MyInterface>();
方法1:
將方法更改為:
public void CallAllMethodsInContainer<T>(MyContainer<T> container) where T : IMyInterface
{
foreach (T myClass in container.Contents)
{
myClass.MyMethod();
}
}
和片段:
MyContainer<MyClass> container = new MyContainer<MyClass>();
container.Contents.Add(new MyClass());
this.CallAllMethodsInContainer(container);
方法2:
或者,將CallAllMethodsInContainer
方法移動到MyContainer<T>
類,如下所示:
public void CallAllMyMethodsInContents()
{
foreach (T myClass in Contents)
{
myClass.MyMethod();
}
}
並將代碼段更改為:
MyContainer<MyClass> container = new MyContainer<MyClass>();
container.Contents.Add(new MyClass());
container.CallAllMyMethodsInContents();
方法3:
編輯:另一種方法是從MyContainer
類中刪除通用約束,如下所示:
public class MyContainer
{
public IList<MyInterface> Contents;
}
並將方法簽名更改為
public void CallAllMethodsInContainer(MyContainer container)
然后該片段應該如下:
MyContainer container = new MyContainer();
container.Contents.Add(new MyClass());
this.CallAllMethodsInContainer(container);
請注意,使用此替代方法,容器的Contents
列表將接受實現MyInterface
任何對象組合。
哇,這個問題最近出現了很多。
簡短的回答:不,這是不可能的。 這是可能的:
public void CallAllMethodsInContainer<T>(MyContainer<T> container) where T : IMyInterface
{
foreach (IMyInterface myClass in container.Contents)
{
myClass.MyMethod();
}
}
這就是為什么你嘗試的是不可能的(取自我最近的答案 ):
考慮List<T>
類型。 假設您有一個List<string>
和一個List<object>
。 string派生自object,但它不遵循List<string>
派生自List<object>
; 如果確實如此,那么你可以擁有這樣的代碼:
var strings = new List<string>();
// If this cast were possible...
var objects = (List<object>)strings;
// ...crap! then you could add a DateTime to a List<string>!
objects.Add(new DateTime(2010, 8, 23));23));
上面的代碼說明了(而不是) 協變類型意味着什么。 注意,如果T
是協變的 ,則可以將類型T<D>
為另一種類型T<B>
,其中D
從B
派生(在.NET 4.0中); 泛型類型是協變的,如果它的泛型類型參數只以輸出的形式出現 - 即只讀屬性和函數返回值。
可以這樣想:如果某種類型T<B>
總是提供B
,那么總是提供D
( T<D>
)的那個將能夠作為T<B>
因為所有D
s都是B
s。
順便提一下,如果類型的泛型類型參數僅以輸入的形式出現 - 即方法參數,則類型是逆變的 。 如果類型T<B>
是逆變的,則可以將其轉換為T<D>
,這可能看起來很奇怪。
可以這樣想:如果某個類型T<B>
總是需要B
,那么它可以介入一個總是需要D
那個,因為所有的D
都是B
s。
您的MyContainer
類既不是協變也不是逆變,因為它的類型參數出現在兩個上下文中 - 作為輸入(通過Contents.Add
)和作為輸出(通過Contents
屬性本身)。
這是協方差的問題
http://msdn.microsoft.com/en-us/library/dd799517.aspx
你不能將MyContainer<MyClass>
為MyContainer<IMyInterface>
因為那時你可以做一些像Contents.Add(new AnotherClassThatImplementsIMyInterface())
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.