为什么IEnumerable<;T>;在C#4中变为协变?

为什么IEnumerable<;T>;在C#4中变为协变?,c#,generics,ienumerable,language-design,covariance,C#,Generics,Ienumerable,Language Design,Covariance,在早期版本的C#IEnumerable中定义如下: public interface IEnumerable<T> : IEnumerable 公共接口IEnumerable:IEnumerable 由于C#4,定义为: public interface IEnumerable<out T> : IEnumerable 公共接口IEnumerable:IEnumerable 这只是为了让LINQ表达式中令人讨厌的角色消失吗 这不也会带来同样的问题吗,比如按顺序使

在早期版本的C#
IEnumerable
中定义如下:

public interface IEnumerable<T> : IEnumerable
公共接口IEnumerable:IEnumerable
由于C#4,定义为:

public interface IEnumerable<out T> : IEnumerable
公共接口IEnumerable:IEnumerable
  • 这只是为了让LINQ表达式中令人讨厌的角色消失吗
  • 这不也会带来同样的问题吗,比如按顺序使用
    string[]:

    这只是为了让LINQ表达式中令人讨厌的角色消失吗

    它使事情表现得像人们通常期望的那样;p

    这不会带来与
    string[]相同的问题吗
    这只是为了让LINQ表达式中令人讨厌的角色消失吗

    不仅是在使用LINQ时。它在任何有
    IEnumerable
    且代码需要
    IEnumerable
    的地方都很有用


    这难道不会带来与
    string[]
    相同的问题吗?Marc和CodeInChaos的答案很好,但只需补充一些细节:

    首先,听起来您有兴趣了解我们制作此功能所经历的设计过程。如果是这样的话,那么我鼓励您阅读我在设计和实现该功能时撰写的一系列长篇文章。从页面底部开始:

    这只是为了让LINQ表达式中令人讨厌的角色消失吗

    不,这不仅仅是为了避免使用
    Cast
    表达式,而是鼓励我们使用此功能的动机之一。我们意识到,“为什么我不能用一个长颈鹿序列来处理一系列动物?”问题的数量会上升,因为LINQ鼓励使用序列类型。我们知道我们想先给
    IEnumerable
    添加协方差

    实际上,我们甚至在C#3中也考虑过将
    IEnumerable
    设置为协变,但我们认为,如果不将整个特性介绍给任何人使用,那么这样做会很奇怪


    这会不会带来与
    string[]相同的问题,因此C#编译器会严格检查C#中的方差注释(与数组不同)?@soc yes;在定义协变或逆变接口/委托时,以及在使用协变分配引用时(例如,将
    IEnumerable
    分配给
    IEnumerable
    )。如果您将
    添加到
    ,则只能有返回此类信息的API;相反地,
    中的
    。因此,属性/索引器不能同时具有该类型的
    get
    set
    ref
    /
    out
    ,很明显,返回类型和参数类型都被检查了。考虑到一个稍微相关的问题的答案,我想知道这是如何工作的:从那以后,是否有任何变化允许MS改变方差?@soc-什么都没有;请注意,只有
    IEnumerable
    提供此功能-在
    IList
    ICollection
    上没有差异,因为它们不兼容;它们有“in”和“out”两种用法,因此既不是协变的也不是逆变的。@在您的示例中,您正在实现接口-您没有
    out
    ,只是
    :IEnumerable
    。另外,
    out
    不适用于类-仅适用于接口和委托/。嘿,感谢您抽出时间!是的,我对设计过程很感兴趣。我想知道的一件事是,不支持协变返回类型和C#/CLR中的“真实”(抱歉)差异对其他语言有多大影响。例如。今天有一篇关于Scala.NET的帖子。我想知道他们对这两个问题做了什么。。。(@soc:几乎每当有人在评论中提出一个非琐碎的问题时,打开一个新的问题是一个好主意。我当然不是Scala或C++/CLI方面的专家,我相信这两个方面都有返回类型协方差,即使CLR本机不支持它。@Eric Lippert:出于好奇,是否有可能vb/c#/.net的未来版本要么允许实现读/写属性的类隐式地被视为具有只读和只读实现,要么允许在协变/逆变接口中使用读写属性,将其标记为只读或只读?在许多情况下,它将用于ul允许IList从协变IReadableList派生,前提是不需要IList的实现添加只读属性。@supercat:任何事情都有可能。我们当然注意到,在类型系统中,我们没有很好的支持来推理类类型的不变性,许多人建议“只读列表”和“只读数组”当协变使用时,类型会更安全。但是我们目前没有针对特定功能的具体计划来解决这些问题;我们只是知道很多人都有这些问题。@Eric Lippert:我不是在考虑不可变的类型,而是现有类型的只读接口(尽管从IReadableList派生的IImmutableList也很有用)。假设我需要一个有效的方法以相反的顺序对IEnumerable中的项进行操作。可以枚举到列表,但如果该项已经是IList,则效率低下。如果我需要一个IEnumerable,但得到了一个列表,有没有办法利用它是IList的事实?如果IList继承了IReadableList,这很容易。理想情况下,将IEnumerable作为IEnumerable传递就像将其作为IEnumerable传递一样,但这并不总是实用的。除非使用反射,例如,否则很难编写“计数”方法,将ICollection作为IEnumerable给定,可以利用ICollection.Count。有人可能会说ICollection的设计很差,但我认为ICollection的设计者没有任何理由
    void DoSomething(IEnumerabe<Base> bla);
    void DoSomething(object blub);
    
    IEnumerable<Derived> values = ...;
    DoSomething(values);
    
    if (x is IEnumerable<Animal>)
        ABC();
    else if (x is IEnumerable<Turtle>)
        DEF();
    
    class B     { public void M(IEnumerable<Turtle> turtles){} }
    class D : B { public void M(IEnumerable<Animal> animals){} }
    
    class Weird : IEnumerable<Turtle>, IEnumerable<Banana> { ... }
    class B 
    { 
        public void M(IEnumerable<Banana> bananas) {}
    }
    class D : B
    {
        public void M(IEnumerable<Animal> animals) {}
        public void M(IEnumerable<Fruit> fruits) {}
    }