C# 为什么我不能将混凝土类型列表指定给该混凝土的列表';用户界面?

C# 为什么我不能将混凝土类型列表指定给该混凝土的列表';用户界面?,c#,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 al

为什么不编译

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) { }

}
公共接口IConcrete{}
公共类混凝土:IConcrete{}
公开课跑者
{
公共静态void Main()
{
var myList=新列表();
DoStuffWithInterfaceList(myList);//编译器不允许这样做
}
公共静态void DoStuffWithInterfaceList(接口列表){}
}
让myList成为正确类型的最快方法是什么

编辑 我把DoStuffWithInterfaceList示例搞砸了

C#目前不支持这样的泛型类型转换(如果我理解正确,C#4将支持它,正如wcoenen在下面的评论中所说的那样,Eric也在他的回答中澄清,使它在C#4中工作的唯一方法是使用
IEnumerable
)。现在,您需要以某种方式转换列表

您可以这样调用该方法:

DoStuffWithInterface(myList.ConvertAll<IConcrete>(n => n as IConcrete));
dostufwithinterface(myList.ConvertAll(n=>n作为IConcrete));
更新
我意识到你可能不需要lambda里面的演员阵容,尽管为了清晰起见我有点喜欢它。因此,这也应该起作用:

DoStuffWithInterface(myList.ConvertAll<IConcrete>(n => n));
dostufwithinterface(myList.ConvertAll(n=>n));

public void dostufwithinterface(IList-concrete){

var myNewList=myList.Cast();
您可以试试

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

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

这与协方差和反方差有关。Eric Lippert在今年早些时候写了很多关于它的文章。(11篇关于这个主题的博客文章)第一篇是。阅读这篇文章,然后在他的博客上搜索其他文章。他详细解释了为什么这类事情很困难


好消息:有些限制在C#4.0中被取消。

目前,这是被禁止的,因为否则类型安全性将被破坏。 您可以在DoStuffWithInterfaceList中执行以下操作:

public class OtherConcrete : IConcrete { }

public void DoStuffWithInterfaceList(List<IConcrete> listOfInterfaces) 
{
       listOfInterfaces.Add(new OtherConcrete ());
}
public类OtherConcrete:IConcrete{}
公共作废Dostuff with InterfaceList(接口列表)
{
添加(新的OtherConcrete());
}
这将在运行时失败,因为ListofInterface仅为Concrete类型

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

要回答关于转换列表的其他问题,如果您使用的是.NET3.5,我将使用Enumerable.Cast扩展方法。否则,您可以使用yield关键字自己编写一个惰性转换方法,这将给您相同的效果

编辑:


正如Eric Lippert所说,为了使IEnumerable在C#4中工作,您应该使用IEnumerable。

IList不起作用,因为IList不是逆变的。它需要是IEnumerable的,尽管这只适用于4.0。你也可以使用一个带有lambda表达式的ConvertAll,它将在3.5中工作,几乎所有的答案都说这将在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
    { }
}
公共类运行程序
{
公共静态void Main()
{
var myList=新列表();
DoStuffWithInterfaceList(myList);//编译器不允许这样做
}
带有接口列表的公共静态无效Dostuff(接口列表)
其中T:i混凝土
{ }
}

请注意,该方法现在是泛型的,并使用类型约束来确保只能使用
IConcrete
子类型的列表调用它。

我知道,我把示例搞糟了,我的意思是所有操作都在列表上,请参阅我的编辑。谢谢。当然C#4也不允许这样做,因为否则DostuffithInterface将能够将任何
IConcrete
实现插入到一个列表中,该列表实际上应该只包含
具体的
实例。这就是Eric所说的不是“协变”的
List
的意思。@wcoenen:我认为(没有用于测试的VS2010)它会工作,但前提是
DoStuffWithInterface
的参数更改为
IList
类型。我确实有用于测试的VS2010。唯一可行的方法是将参数类型更改为
IEnumerable
或其他协变类型<代码>IList
不是协变的。@wcoenen:谢谢您的澄清。迫不及待地想得到我的新电脑,这样我就可以安装VS2010来测试和验证C#4问题。(顺便说一句:由于上述解决方案在这种情况下总是有效的,在C#4中引入通用协方差可能会被证明是过火了)老兄,这一点现在看来非常明显。谢谢。有人对此有任何更新吗?十年后仍然是同样的答案吗?
public void DoStuffWithInterface(IList<IConcrete> concrete) { }
public void DoStuffWithInterface(IList concrete) { }
public class OtherConcrete : IConcrete { }

public void DoStuffWithInterfaceList(List<IConcrete> listOfInterfaces) 
{
       listOfInterfaces.Add(new OtherConcrete ());
}
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
    { }
}