给出(非常简单的)代码。
public class Class1
{
}
public class Class2 : Class1
{
}
public class List1 : System.Collections.Generic.IEnumerable<Class1>
{
public new System.Collections.Generic.IEnumerator<Class1> GetEnumerator()
{
yield return new Class1();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
public class List2 : List1 , System.Collections.Generic.IEnumerable<Class2>
{
public new System.Collections.Generic.IEnumerator<Class2> GetEnumerator()
{
yield return new Class2();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
然后是代码
var l = new List2();
var first = l.First();
不会编译,但会给出错误
'List2'不包含'First'的定义,也没有扩展方法'First'接受类型'List2'的第一个参数(你是否缺少using指令或汇编引用?)
如果List2不是从List1派生的,那么它编译好,这证明List2确实有一个有效的扩展方法。
这只是一个误导性错误的情况,问题是它有两个扩展方法,并且不知道选择哪一个?
如果是这样,为什么不能告诉Class2是更具体的版本,就像编译器使用方法重载解析一样?
答案 0 :(得分:7)
问题是List2
同时实现IEnumerable<Class1>
和IEnumerable<Class2>
,因此编译器不知道调用Enumerable.First<Class1>
或Enumerable.First<Class2>
中的哪一个。实际上,编译器可以找到调用T
的最佳Enumerable.First<T>(l)
,因此它不会将任何通用Enumerable.First<T>
计为合格方法。因此,它现在只查找名为First
的非泛型方法,其中l
可以隐式转换为单个参数,并且找不到任何参数。
你可以明确并说出
var first = l.First<Class1>();
或
var first = l.First<Class2>();
然后你没事。
答案 1 :(得分:3)
考虑这个课程:
public class List1 : IEnumerable<string>, IEnumerable<object>
{
IEnumerator<object> IEnumerable<object>.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<string> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
所以编译器不知道要调用IEnumerable<string>
或IEnumerable<object>
的First()方法。这个例子是你情况的简化版本。
考虑到你已经在没有源代码的类上编写了扩展方法,但是在下一个版本中,它们在该类上实现了完全相同的方法。在这种情况下,您的旧代码不再可靠,因此最佳解决方案是编译时错误。
答案 2 :(得分:-1)
当您的班级继承自List1
和IEnumerable<Class2>
时,它实际上会继承3种类型:List1
,IEnumerable<Class1>
和IEnumerable<Class2>
(以及{{1} })。这意味着你的类有两个不同的object
实现,因为编译器不会生成不带泛型参数的“默认”方法,因为它无法弄清楚T是什么。
这并不意味着First<>()
不可用,只是意味着您必须指定T是什么。
请考虑以下代码段:
First<>()
var l2 = new List2();
l2.First<Class1>();
l2.First<Class2>();
和First<Class1>()
都可用,从那以后编译器无法弄清楚First<Class2>()
是什么。
但是在这段代码中:
T
编译器可以认为l2.First((Class2 c) => c.ToString() == "");
是T
,所以下面的编译就好了。
尽管前面的方法是有效的,但是让一个类继承两次相同的接口并不是一个好的设计实践。 (在你的情况下,一次通过Class2
,第二次通过Class1
的显式继承)。为了更好的设计,您可以实现一个通用的抽象类,并使您的列表从此类派生。
IEnumerable<Class2>