我有一个具有
签名的函数Update(IEnumberable<INotifyPropertyChanging> things)
和一个班级
doohickey : INotifyPropertyChanging, INotifyPropertyChanging{}
当我尝试
时List<doohickey> doohickeys = new List<doohickey>();
Update(doohickeys);
抛出一个异常说明:
cannot convert from 'System.Collections.Generic.List<doohickey>' to 'System.Collections.Generic.IEnumerable<System.ComponentModel.INotifyPropertyChanging>'
给出了什么? Doohickey继承了INotifyPropertyChanging接口,List继承了IEnumerable接口!为什么简单地传递对象并期望它被抛弃呢?
我已经包含了对INotifyPropertyChanging的引用,以表示doohickey实际上是一个linq-to-sql类;如果上下文很重要。
答案 0 :(得分:3)
这不起作用,因为List<doohickey>
和IEnumerable<NotifyPropertyChanging>
之间没有隐式转换。你需要打电话:
Update(doohickeys.Cast<INotifyPropertyChanging>());
背后的原因是类型安全。在C#4中,语言中添加了共同/逆变,这通常有助于使这些类型的转换有效。但是存在一些限制,因为涉及的类型需要声明为co / contravariant,并且它仅适用于接口和委托。
您无法将List<T>
隐式转换为List<TBase>
的原因是,它会使此代码有效:
List<T> values = new List<T>();
// add some values...
List<TBase> bases = values;
bases.Add(new TBase()); // Woops. We broke type safety by adding a TBase to a list of T's
现在,如果您在values
类型为IEnumerable<T>
时尝试执行此操作,则它在C#4中有效。这是因为您只能获取IEnumerable<T>
的值,因此我们无需担心添加较少的类型。因此IEnumerable<T>
是协变的,并声明为:
IEnumerable<out T>
out
表示“协变”。 (关键字实际上是记住变量类型值的最佳方式。
共同/逆变是一个相当大的主题,but this article does a good job of explaining the most important parts。如果您想了解更多信息,Eric Lippert对该功能有一个11-part blog post series。 Eric是语言设计师之一。