为什么我不能将具体类型列表分配给该具体界面的列表?

时间:2009-11-20 19:04:02

标签: c#

为什么这不编译?

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 设置为正确类型的最快方法是什么?

EDIT 我弄乱了DoStuffWithInterfaceList示例

8 个答案:

答案 0 :(得分:18)

几乎所有这些答案都说这将在C#4中得到支持。他们都错了。

为了清楚明白:这不是我们将在C#4中支持的协方差的一个例子,因为这样做不会是类型安全的。我们支持类型安全的协方差和逆变使用引用类型参数构造的通用接口和委托。这里的示例使用类类型List,而不是接口类型。并且接口类型IList对于协方差或逆变不是类型安全的。

IEnumerable将是协变的,因为它是一个对协方差安全的接口。

答案 1 :(得分:10)

对于大型列表而言,已接受的解决方案效率非常低,而且完全没有必要。您可以稍微更改方法的签名,以使代码无需任何隐式或显式转换

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个子类型的列表来调用它。

答案 2 :(得分:4)

目前,这是禁止的,否则将破坏类型安全性。  你可以在DoStuffWithInterfaceList中做这样的事情:

public class OtherConcrete : IConcrete { }

public void DoStuffWithInterfaceList(List<IConcrete> listOfInterfaces) 
{
       listOfInterfaces.Add(new OtherConcrete ());
}

哪个会在运行时失败,因为listOfInterfaces只是Concrete类型。

正如其他人所说,只要您不更改方法内的列表,就可以使用C#4,但是您必须明确告诉编译器。

要回答有关转换列表的其他问题,如果您使用.Net 3.5,我会使用Enumerable.Cast&lt;&gt;扩展方法。否则,您可以使用yield关键字自己编写一个惰性转换方法,这将给您带来相同的效果。

修改

正如Eric Lippert所说,你应该使用IEnumerable来使它在C#4中工作。

答案 3 :(得分:2)

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));

答案 4 :(得分:2)

这与协方差和逆变有关。 Eric Lippert在今年早些时候写了很多关于它的文章。 (11篇专门针对该主题的博客文章。)第一篇是Covariance and Contravariance in C#, Part One。阅读并阅读他的博客,了解其余部分。他详细解释了为什么这种事情很困难。

好消息:C#4.0中解除了一些限制。

答案 5 :(得分:2)

IList不起作用,因为IList不是逆变的。它需要是IEnumerable,但这只适用于4.0。您也可以使用带有lambda表达式的ConvertAll,它将在3.5

中运行

答案 6 :(得分:1)

你可以尝试

public void DoStuffWithInterface(IList<IConcrete> concrete) { }

但我认为这只适用于.NET 4.0。

如果你想变脏,那就

public void DoStuffWithInterface(IList concrete) { }

并检查出来的物体是否具体。

答案 7 :(得分:0)

foreach (var item in myList)
    DoStuffWithInterface(item);

public void DoStuffWithInterface(IList<IConcrete> concrete) { }

var myNewList = myList.Cast<IConcrete>();