![](/img/trans.png)
[英]Why can't I add concrete classes to a list of interfaces which the concrete classes implement?
[英]Why can I not assign a List of concrete types to a List of that concrete's interface?
為什么這不編譯?
public interface IConcrete { }
public class Concrete : IConcrete { }
public class Runner
{
public static void Main()
{
var myList = new List<Concrete>();
DoStuffWithInterfaceList(myList); // compiler doesn't allow this
}
public static void DoStuffWithInterfaceList(List<IConcrete> listOfInterfaces) { }
}
什么是將myList設置為正確類型的最快方法?
編輯我弄亂了DoStuffWithInterfaceList示例
幾乎所有這些答案都說這將在C#4中得到支持。他們都錯了。
簡單來說: 這不是我們將在C#4中支持的協方差的一個例子,因為這樣做不會是類型安全的。 我們支持使用引用類型參數構造的泛型接口和委托的類型安全協方差和逆變 。 這里的示例使用類類型List,而不是接口類型。 並且接口類型IList對於協方差或逆變不是類型安全的。
IEnumerable將是協變的,因為它是一個對協方差安全的接口。
對於大型列表,接受的解決方案效率非常低,而且完全沒有必要。 您可以稍微更改方法的簽名,以使代碼無需任何隱式或顯式轉換即可運行:
public class Runner
{
public static void Main()
{
var myList = new List<Concrete>();
DoStuffWithInterfaceList(myList); // compiler doesn't allow this
}
public static void DoStuffWithInterfaceList<T>(List<T> listOfInterfaces)
where T: IConcrete
{ }
}
請注意,該方法現在是通用的,並使用類型約束來確保只能使用IConcrete
子類型列表調用它。
目前,這是禁止的,否則類型安全將被打破。 你可以在DoStuffWithInterfaceList中做這樣的事情:
public class OtherConcrete : IConcrete { }
public void DoStuffWithInterfaceList(List<IConcrete> listOfInterfaces)
{
listOfInterfaces.Add(new OtherConcrete ());
}
哪個會在運行時失敗,因為listOfInterfaces只是Concrete類型。
正如其他人所說,只要您不更改方法內的列表,就可以使用C#4,但是您必須明確告訴編譯器。
要回答有關轉換列表的其他問題,如果您使用的是.Net 3.5,我將使用Enumerable.Cast <>擴展方法。 否則,你可以使用yield關鍵字自己編寫一個惰性轉換方法,這將給你相同的效果。
編輯:
正如Eric Lippert所說,你應該使用IEnumerable來使它在C#4中工作。
C#目前不支持轉換類似的泛型類型(
如果我理解正確的話,它將在C#4中得到支持
正如wcoenen在下面的評論中所述,並且Eric在他的回答中也澄清了,使它在C#4中工作的唯一方法是使用IEnumerable<IConcrete>
。 現在,您需要以某種方式轉換列表。
您可以像這樣調用方法:
DoStuffWithInterface(myList.ConvertAll<IConcrete>(n => n as IConcrete));
更新
我意識到你可能不需要在lambda中使用cast,盡管為了清晰起見我有點喜歡它。 所以這也應該有效:
DoStuffWithInterface(myList.ConvertAll<IConcrete>(n => n));
這與協方差和逆變有關。 Eric Lippert在今年早些時候寫了很多關於它的文章。 (11篇專門針對該主題的博客文章。)第一篇是C#中的協方差和逆變,第一部分 。 閱讀並閱讀他的博客,了解其余部分。 他詳細解釋了為什么這種事情很困難。
好消息:C#4.0中解除了一些限制。
IList不起作用,因為IList不是逆變的。 它需要是IEnumerable,但這只適用於4.0。 您也可以使用帶有lambda表達式的ConvertAll,它將在3.5中工作
你可以試試
public void DoStuffWithInterface(IList<IConcrete> concrete) { }
但我認為這只適用於.NET 4.0。
如果你想變臟,就去做吧
public void DoStuffWithInterface(IList concrete) { }
並檢查出來的物體是否具體。
foreach (var item in myList)
DoStuffWithInterface(item);
要么
public void DoStuffWithInterface(IList<IConcrete> concrete) { }
要么
var myNewList = myList.Cast<IConcrete>();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.