5

给定(非常简单的)代码。

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”的定义,并且找不到接受“List2”类型的第一个参数的扩展方法“First”(您是否缺少 using 指令或程序集引用?)

如果 List2 不是从 List1 派生的,那么它可以编译,这证明 List2 确实具有有效的扩展方法。

这仅仅是一个误导性错误的情况吗?问题是它有两种扩展方法,不知道该选择哪一种?

如果是这样,为什么它不能告诉 Class2 是更具体的版本,就像编译器使用方法重载解析一样?

4

3 回答 3

7

问题是它List2同时实现了IEnumerable<Class1>and IEnumerable<Class2>,所以编译器不知道调用哪个Enumerable.First<Class1>or Enumerable.First<Class2>。实际上,T编译器没有找到最好的调用方法Enumerable.First<T>(l),因此它不会将任何泛型Enumerable.First<T>视为合格方法。因此,它现在只寻找一个以First单个参数命名的非泛型方法,该方法l可以隐式转换为,但它找不到任何方法。

你可以明确地说

var first = l.First<Class1>();

或者

var first = l.First<Class2>();

然后你就没事了。

于 2013-05-29T16:27:42.977 回答
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() 方法。这个例子是你的情况的简化版本。

假设您在一个没有源代码的类上编写了扩展方法,但在下一个版本中,它们在该类上实现了完全相同的方法。在这种情况下,您的旧代码不再可靠,因此最好的解决方案是编译时错误。

于 2013-05-29T16:39:10.337 回答
-1

泛型方法有什么问题?

当你的类继承自它List1并从IEnumerable<Class2>它实际上继承自 3 种类型时:List1IEnumerable<Class1>IEnumerable<Class2>object)。这意味着您的类有 2 个不同的实现,First<>()因为编译器不会生成不接受通用参数的“默认”方法,因为它无法弄清楚 T 是什么。

这并不意味着First<>()不可用,只是意味着您必须指定 T 是什么。

考虑以下代码片段:

        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>)。为了更好的设计,您可以实现一个通用抽象类,并使您的列表派生自此类。

public abstract class CommonListBase
{

}

public class List1 : CommonListBase, IEnumerable<Class1>
{
    public System.Collections.Generic.IEnumerator<Class1> GetEnumerator()
    {
        yield return new Class1();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}


public class List2 : CommonListBase, IEnumerable<Class2>
{
    public System.Collections.Generic.IEnumerator<Class2> GetEnumerator()
    {
        yield return new Class2();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}
于 2013-05-29T16:41:30.120 回答