- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我一直在学习 C# 中的协变和逆变,但我无法理解:
class Program
{
static void Main(string[] args)
{
PersonCollection<Person> personCollection = new PersonCollection<Person>();
IMyCollection<Teacher> myCollection = personCollection; // Here's the error:
// Cannot implicitly convert type 'PersonCollection<Teacher>' to 'IMyCollection<Person>'
}
}
class Person { }
class Teacher : Person { }
interface IMyCollection<T> { }
class PersonCollection<T> : IMyCollection<T> { }
众所周知,我们可以将派生类的实例隐式转换为基类。因此,在上面的代码中,虽然“Teacher”类派生自“Person”类,但 IMyCollection<Teacher>
无法转换为 IMyCollection<Person>
,为什么?!
注意:我想知道原因,而不是解决方案。
最佳答案
Note: I want to know the reason, not the solution
虽然这正是存在反协方差的原因,但让我用您的示例快速向您展示一个解释,强调为什么这不起作用:
让我们假设以下设置代码:
PersonCollection<Person> personCollection = new PersonCollection<Person>();
personCollection.Add(new Teacher("Teacher A"));
personCollection.Add(new Teacher("Teacher B"));
personCollection.Add(new Student("Student A"));
personCollection.Add(new Student("Student B"));
personCollection.Add(new Student("Student C"));
personCollection.Add(new Student("Student D"));
所以现在,我有一个 PersonCollection<Person>
有两个Teacher
和四个 Student
对象(此处 Student
也继承自 Person
)。这是完全有效的,因为任何 Teacher
和 Student
也是一个Person
.所以我可以将元素添加到集合中。
现在,假设允许以下内容:
IMyCollection<Teacher> myCollection = personCollection;
现在,我有一个 myCollection
其中显然包含 Teacher
对象。但由于这只是一个引用分配,myCollection
仍然是与 personCollection
相同的 集合.
所以 myCollection
将包含四个 Student
对象,尽管它的契约(Contract)定义它只包含 Teacher
元素。接口(interface)契约应该完全允许执行以下操作:
Teacher teacher = personCollection[4];
但是personCollection[4]
是C同学,显然这样不行。
由于编译器无法在此项目分配期间进行此验证,并且由于我们需要类型安全而不是运行时验证,因此编译器可以防止这种情况的唯一明智方法是不允许您将集合转换为 IMyCollection<Teacher>
。 .
你可以让你的 IMyCollection<T>
通过将其声明为 IMyCollection<in T>
来实现逆变这将解决您的情况并允许您进行该分配,但同时它会阻止您检索 Teacher
反对它,因为它不是协变的。
通常,从集合中设置和检索泛型值的唯一方法是使它不变(这是默认设置),这也是为什么 BCL 中的所有泛型集合都是不变的,只有一些接口(interface)是相反的或协变(例如 IEnumerable<T>
是协变的,因为它只是关于检索值)。
由于您将问题中的错误更改为“无法将类型‘PersonCollection’隐式转换为‘IMyCollection’”,让我也解释一下这个案例(将这个答案变成一个完整的反&协方差答案 *sigh*...)。
因此代码如下:
PersonCollection<Teacher> personCollection = new PersonCollection<Teacher>();
IMyCollection<Person> myCollection = personCollection;
再次,让我们假设这是有效的并且有效。所以现在,我们有一个 IMyCollection<Person>
我们可以合作!所以让我们在这里添加一些人:
myCollection.Add(new Teacher("Teacher A"));
myCollection.Add(new Teacher("Teacher B"));
myCollection.Add(new Student("Student A"));
糟糕!实际集合还是一个PersonCollection<Teacher>
只能带Teacher
对象。但是 IMyCollection<Person>
type 允许我们添加 Student
物也是人!所以这会在运行时失败,而且,由于我们希望在编译时保证类型安全,编译器必须不允许在此处进行赋值。
这种分配只对协变有效 IMyCollection<out T>
但这也不允许我们添加 T
类型的元素。到它(出于与上述相同的原因)。
现在,而不是添加到 PersonCollection<Teacher>
在这里,让我们使用
关于c# - 为什么泛型类型参数中的隐式子到父转换是不可能的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49476202/
我是一名优秀的程序员,十分优秀!