繁体   English   中英

不能在派生类的IEnumerable基类上使用LINQ方法

[英]Cannot use LINQ methods on IEnumerable base class from derived class

我试图在从已经实现IEnumerable<Animal>的基类派生的类中实现IEnumerable<Turtle> IEnumerable<Animal>

为什么在类Turtle任何方法中调用base.Cast<Turtle>() (或基本元素上的任何LINQ方法)都无法编译?

this替换base是不可能的,因为它显然会导致StackOverflowException

以下是复制问题的最小代码示例:

public interface IAnimal {}

public class Animal : IAnimal {}

public class Turtle : Animal {}

public class AnimalEnumerable : IEnumerable<Animal> {
    List<Animal> Animals = new List<Animal>();
    IEnumerator<Animal> IEnumerable<Animal>.GetEnumerator() {
        return Animals.GetEnumerator();
    }

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

public class TurtleEnumerable : AnimalEnumerable, IEnumerable<Turtle> {
    IEnumerator<Turtle> IEnumerable<Turtle>.GetEnumerator() {
        return base.Cast<Turtle>().GetEnumerator(); //FAILS WITH "CANNOT RESOLVE SYMBOL Cast"
    }
}

出于某种原因,替换base.Cast<Turtle>().GetEnumerator(); with this.OfType<Animal>().Cast<Turtle>().GetEnumerator(); 在没有抛出StackOverflowException ,但我不知道为什么。

在给出其他答案的情况下,代码存在许多问题。 我想回答你的具体问题:

为什么在类Turtle任何方法中调用base.Cast<Turtle>() (或基本元素上的任何LINQ方法)都无法编译?

我们来看规范,第7.6.8节。

base-access用于访问在当前类或结构中由类似命名的成员隐藏的基类成员。

您是否正在访问基类成员? 扩展方法是静态类的成员,它包含扩展方法,而不是基类。

仅允许在实例构造函数,实例方法或实例访问器的块中进行基本访问。

你在这里很好

当base.I出现在类或结构中时,我必须表示该类或结构的基类的成员。

同样, Cast<T>不是基类的成员。

当base-access引用虚函数成员(方法,属性或索引器)时,将确定在运行时调用哪个函数成员(第7.5.4节)。

您无法访问任何虚拟内容。 扩展方法是静态的。

调用的函数成员是通过查找关于B的函数成员的最多派生实现来确定的(而不是相对于其的运行时类型,如非基本访问中通常的那样)。 因此,在虚函数成员的重写中,可以使用基本访问来调用函数成员的继承实现。

现在我们看到基本访问的目的是什么: 启用对当前类型中覆盖的虚拟成员的非虚拟分派 ,或者调用当前类型中的new成员隐藏的基类成员 这不是你想要使用的base ,因此你注定要失败。 像这样停止使用base off-label。 仅在尝试对虚拟成员执行非虚拟分派或访问隐藏成员时才使用它。

埃里克利珀已经声明之前,这是一个有点意识的设计决定在这里 在您首先可以访问基类实现的情况下,您从未打算使用扩展方法。

如果你考虑一下,你也不需要在这里做到这一点。 创建受保护的GetEnumerator属性或方法并使用它! 基本面向对象; 没有必要在这里折磨linq。

编辑:有人指出,我以前的建议没有用。 因此,我建议不要实现两个不同的IEnumerable接口,因为这将导致很多令人头疼的foreach无论如何。

我开始相信这个实现可能是你真正想要的:

public interface IAnimal { }

public class Animal : IAnimal { }

public class Turtle : Animal { }

public class AnimalEnumerable : IEnumerable<Animal>
{
    IEnumerator<Animal> IEnumerable<Animal>.GetEnumerator()
    {
        throw new NotImplementedException();
    }


    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}

public class TurtleEnumerable : AnimalEnumerable
{
}

然后,您可以通过Animal及其衍生物进行枚举

我会回答这个问题:

为什么在类Turtle中的任何方法中调用base.Cast()(或基本元素上的任何LINQ方法)都无法编译?

该异常的原因是Cast和其他此类方法是扩展方法 扩展方法是静态的。

例如,让我们看一下:

public static class Extensions
{
    public static void Method2(this Base b) ...
}

public class Base 
{
    public void Method1() ...
}

public class Derived:Base
{
    public void Test()
    {
        base.Method1();

        base.Method2(); // Does not contain a definition
    }
}

如你所知,扩展方法是一个非常好的语法糖。 它们并没有真正添加到类中,但编译器让它感觉像它们一样。 因此,编译器会将该行代码更改为:

 Extensions.Method2(base); 

如果用您的代码替换代码,编译器将提供更合适的错误消息: 在此上下文中使用关键字base无效。

MSDN所述:

仅在构造函数,实例方法或实例属性访问器中允许基类访问。

您的方法存在几个问题。 TurtleEnumerable实现了IEnumerable<Animal>IEnumerable<Turtle> 为了能够在foreach循环中使用TurtleEnumerable实例,您必须将其转换为要编译的代码:

foreach (var turtle in (IEnumerable<Turtle>) turtleEnumerable)

您还使用显式接口实现来隐藏通用GetEnumerator()方法。 您必须这样做,因为您不能单独对返回类型执行重载解析,并且两个通用GetEnumerator()方法仅因返回类型而不同。

但是,这意味着TurtleEnumerable方法无法调用基本的GetEnumerator()方法。 这样做的原因是base行为不像“base”类型的变量。 相反,它是一个保留字,只能用于调用基类方法。 对此的推论是扩展方法不能与base一起使用。 此外,你可以不投base在基类,所以显式接口实现不是通过调用base

但是,你可以投this ,而是因为一般GetEnumerator()TurtleEnumerable隐藏了通用GetEnumerator()AnimalEnumerable您将无法调用到基类,所以你会得到一个堆栈溢出,因为在某些时候执行TurtleEnumerable.GetEnumerator()将调用相同的GetEnumerator

要使代码编译,您需要在基类中创建protected IEnumerator<Animal> GetEnumerator()方法,并创建自己的TurtleEnumerator类,它包装您可以通过调用protected方法获得的基本枚举器实例。

public class TurtleEnumerable : AnimalEnumerable, IEnumerable<Turtle> {

  IEnumerator<Turtle> IEnumerable<Turtle>.GetEnumerator() {
    return new TurtleEnumerator(base.GetEnumerator());
  }

  sealed class TurtleEnumerator : IEnumerator<Turtle> {

    IEnumerator<Animal> animalEnumerator;

    public TurtleEnumerator(IEnumerator<Animal> animalEnumerator) {
      this.animalEnumerator = animalEnumerator;
    }

    public Turtle Current {
      get { return (Turtle) animalEnumerator.Current; }
    }

    Object IEnumerator.Current {
      get { return Current; }
    }

    public Boolean MoveNext() {
      return animalEnumerator.MoveNext();
    }

    public void Reset() {
      animalEnumerator.Reset();
    }

    public void Dispose() {
      animalEnumerator.Dispose();
    }

  }

}

总而言之, IEnumerable<Base>IEnumerable<Derived>都有一个集合,会让你遇到很多麻烦。 你想通过这个设计实现什么目标?

使用通用List<T>和逆变你可以做这样的事情:

IEnumerable<Turtle> turtles = new List<Turtle>();
IEnumerable<Animal> animals = (IEnumerable<Animal>) turtles;

如果需要,您还可以使用自己的通用集合类型替换List<T>

为什么要在TurtleEnumerator类上实现IEnumerable? 此外,我不认为在实现IEnumerable接口时AnimalEnumerable上的可访问性是正确的。

不会实现这样的事情:

    public interface IAnimal { }

    public class Animal : IAnimal { }

    public class Turtle : Animal { }

    public class AnimalEnumerable : IEnumerable<Animal>
    {
        protected List<Animal> Animals = new List<Animal>();

        public IEnumerator<Animal> GetEnumerator()
        {
            return Animals.GetEnumerator();
        }

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

    public class TurtleEnumerable : AnimalEnumerable
    {
        public void AddTurtle(Turtle turtle)
        {
            Animals.Add(turtle);
        }

        public IEnumerable<Turtle> GetTurtles()
        {
            var iterator = GetEnumerator();

            yield return iterator.Current as Turtle;
        }
    }

    [Test]
    public void CanAddTurtles()
    {
        Turtle one = new Turtle();
        Turtle two = new Turtle();

        TurtleEnumerable turtleStore = new TurtleEnumerable();

        turtleStore.AddTurtle(one);
        turtleStore.AddTurtle(two);

        foreach (var turtle in turtleStore.GetTurtles())
        {
            // Do something with the turtles....
        }
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM