Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/csharp-4.0/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么实现变量接口的类保持不变?_C#_C# 4.0_Ienumerable_Covariance - Fatal编程技术网

C# 为什么实现变量接口的类保持不变?

C# 为什么实现变量接口的类保持不变?,c#,c#-4.0,ienumerable,covariance,C#,C# 4.0,Ienumerable,Covariance,C#4.0进一步扩展了泛型类型和接口的协变和逆变。有些接口(如IEnumerable)是协变的,因此我可以执行以下操作: IEnumerable<object> ie = new List<string>(); IEnumerable ie=new List(); 但是这条线呢?我有一个编译时错误 List<Object> list = new List<String>(); //Cannot implicitly convert type L

C#4.0进一步扩展了泛型类型和接口的协变和逆变。有些接口(如
IEnumerable
)是协变的,因此我可以执行以下操作:

IEnumerable<object> ie = new List<string>();
IEnumerable ie=new List();
但是这条线呢?我有一个编译时错误

List<Object> list = new List<String>();
//Cannot implicitly convert type List<string>' to List<object>'
List List=新列表();
//无法将类型列表“隐式转换为列表”
我的意思是,如果
List
实现
IEnumerable
为什么
List
仍然不变?有没有一个很好的反例来解释为什么C#中不允许这样做?

首先,类在C#中总是不变的。不能像这样声明类:

// Invalid
public class Foo<out T>
你希望最后一行做什么?基本上,您不应该向实际执行时间类型为
List
的对象添加
Apple
引用。如果你在一串香蕉上加一个苹果,它就会掉下来。相信我,我已经试过了

最后一行在类型方面应该是安全的,
列表
中的唯一值应该是
null
或对
Banana
或子类实例的引用

至于为什么类即使在逻辑上可以是协变的时候也不能是协变的。。。我相信这会在实现层面带来问题,在编程层面也会有很大的限制。例如,考虑这一点:

public class Foo<out T> // Imagine if this were valid
{
    private T value;

    public T Value { get { return value; } }

    public Foo(T value)
    {
        this.value = value;
    }
}
public class Foo//想象一下这是否有效
{
私人T值;
公共T值{get{return Value;}}
公共食品(T值)
{
这个值=值;
}
}
这可能仍然是无效的-变量仍然是可写的,这意味着它将被视为“in”插槽。您必须将
T
类型的每个变量设置为只读。。。这只是开始。我强烈怀疑会有更深层次的问题


就纯粹的实用主义而言,CLR支持v2-C#4中的委托和接口变体,只是引入了语法来公开该特性。我不相信CLR曾经支持泛型类差异。

如果我们想讨论在列表中添加(接受)某些内容(t),我们必须讨论逆变(协变不允许接受),因此:

List bunchOfFruits=newlist();
//如果列表在T中是逆变的,则这是有效的
列出Bunchoffbananas=bunchOfFruits;
bunchOfBananas.Add(新苹果());//不可能!我们有编译错误,因为尽管苹果是水果,但它不是香蕉的祖先。没有逻辑错误。
bunchOfBananas.Add(新的BigBanana());//可能是因为C的规则!我们和香蕉的后代打交道
bunchOfBananas.Add(新水果());//可能,因为列表的相反。我们与香蕉的祖先打交道。没有逻辑错误。

因此,正如我们所看到的,对于类以及接口和委托(一般来说,类,而不仅仅是集合),差异也应该起作用。我认为它可以在未来的.NET版本中实现。

Jon,这是如何解释有一个水果列表,您可以添加从水果继承的类的实例的呢?例如,
List fruitBowl=new List();添加(新苹果());水果碗。加入(新香蕉())我在过去做过这件事,它的行为总是如预期的那样。为什么CLR不查看水果碗的类型?是不是因为你先将你的
水果碗的值设置为一个香蕉列表,该列表与
水果列表是协变的,然后尝试向同一个集合添加一个不变的
Apple
。说
列表
列表
,这是不正确的,因为你不能在
列表
中添加任意的结果。我不确定我是否理解你的问题。也许在一个新的帖子里问一下,这是否有用?(评论线程对此并不理想。)我不同意Jon的观点,我认为他是错误的,若我们讨论协方差,那个么我们必须使用一个列表的方法,返回T。但Jon是关于接受T的方法ADD,所以我们需要讨论逆变,在这种情况下,所有事情都会以正确的方式进行,所以你们认为不可能从列表中得到一个值?不是这样,塞维,是的。但我们这里讨论的不是集合,而是类差异!
public class Foo<out T> // Imagine if this were valid
{
    private T value;

    public T Value { get { return value; } }

    public Foo(T value)
    {
        this.value = value;
    }
}
List<Fruit> bunchOfFruits = new List<Fruit>();
// This would be valid if List<T> were contravariant in T
List<Banana> bunchOfBananas = bunchOfFruits;
bunchOfBananas.Add(new Apple()); // not possible! We have compile error, coz in spite of Apple is a Fruit it is not ancestor of Banana. No logical mistakes.
bunchOfBananas.Add(new BigBanana()); // possible coz of rules of C#! we deal with a descendant of Banana
bunchOfBananas.Add(new Fruit()); // possible, coz CONTRAVARIANCE of List. We deal with the ancestor of Banana. No logical mistakes.