代码之家  ›  专栏  ›  技术社区  ›  JeremyWeir

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

c#
  •  8
  • JeremyWeir  · 技术社区  · 15 年前

    为什么不编译?

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

    最快的方法是什么 我的名单 正确的类型?

    编辑 我把dostuffw和interfacelist的例子搞砸了

    8 回复  |  直到 15 年前
        1
  •  10
  •   Konrad Rudolph    15 年前

    对于大列表来说,公认的解决方案效率很低,完全没有必要。您可以稍微更改方法的签名以使代码工作。 没有 任何隐式或显式转换:

    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
  •  17
  •   Eric Lippert    15 年前

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

    只是为了清晰起见: 这不是我们将在C 4中支持的协方差示例,因为这样做不会是类型安全的。 我们支持 用引用类型参数构造的泛型接口和委托的类型安全协方差和反方差 . 这里的示例使用的是类类型、列表,而不是接口类型。接口类型ilist对于协变或逆变来说不是类型安全的。

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

        3
  •  4
  •   Moshe Levi    15 年前

    目前,这是禁止的,因为否则类型安全将被破坏。 你可以在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关键字自己编写一个懒惰的转换方法,这将给您同样的效果。

    编辑:

    正如EricLippert所说,您应该使用IEnumerable以便它在C 4中工作。

        4
  •  2
  •   Jim Mischel    15 年前

    这与协方差和反方差有关。埃里克·利珀特今年早些时候写了很多关于它的文章。(11篇关于该主题的博客文章。)第一篇是 Covariance and Contravariance in C#, Part One . 读这篇文章,然后在他的博客中搜索其他文章。他详细解释了为什么这类事情很困难。

    好消息:C 4.0中取消了一些限制。

        5
  •  2
  •   spinon    15 年前

    伊利斯特不会工作,因为伊利斯特不是反义词。它需要是IEnumerable,但同样,它只在4.0中工作。您也可以将convertall与lambda表达式一起使用,它将在3.5中工作。

        6
  •  2
  •   Fredrik Mörk    15 年前

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

    您可以这样调用方法:

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

    更新
    我意识到你可能不需要lambda里面的石膏,尽管我有点喜欢它。所以这也应该有效:

    DoStuffWithInterface(myList.ConvertAll<IConcrete>(n => n));
    
        7
  •  1
  •   HaxElit    15 年前

    你可以试试

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

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

    如果你想变脏,就做吧

    public void DoStuffWithInterface(IList concrete) { }
    

    检查出来的物体是否是混凝土。

        8
  •  0
  •   Daniel A. White    15 年前
    foreach (var item in myList)
        DoStuffWithInterface(item);
    

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

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