15

I am trying to implement IEnumerable<Turtle> in a class deriving from a base class that already implements IEnumerable<Animal>.

Why will calling base.Cast<Turtle>() (or any LINQ method on the base element) in any method from the class Turtle fail to compile?

It is not possible to replace base with this as it obviously results in a StackOverflowException.

Here is a minimal code sample to replicate the issue:

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"
    }
}

For some reason, replacing base.Cast<Turtle>().GetEnumerator(); with this.OfType<Animal>().Cast<Turtle>().GetEnumerator(); works without throwing a StackOverflowException, but I have no idea why.

Erwin Mayer
  • 18,076
  • 9
  • 88
  • 126
  • How is that supposed to work? These are different classes - TurtleEnumerable does not inherit from Turtle in any way, it just implements the interface IEnumerable – srandppl Jan 29 '16 at 11:41
  • TurtleEnumerable does inherit from the class AnimalEnumerable, so it should have access to base elements. – Erwin Mayer Jan 29 '16 at 11:43
  • 1
    Something similar: http://stackoverflow.com/questions/27883427/why-cant-i-call-an-extension-method-from-a-base-class-of-the-extended-type – romanoza Jan 29 '16 at 11:52

5 Answers5

10

There are numerous problems with the code given that other answers get into. I want to answer your specific question:

Why will calling base.Cast<Turtle>() (or any LINQ method on the base element) in any method from the class Turtle fail to compile?

Let's go to the specification, section 7.6.8.

A base-access is used to access base class members that are hidden by similarly named members in the current class or struct.

Are you accessing a base class member? NO. An extension method is a member of the static class that contains the extension method, not the base class.

A base-access is permitted only in the block of an instance constructor, an instance method, or an instance accessor.

You're fine here.

When base.I occurs in a class or struct, I must denote a member of the base class of that class or struct.

Again, Cast<T> is not a member of the base class.

When a base-access references a virtual function member (a method, property, or indexer), the determination of which function member to invoke at run-time (§7.5.4) is changed.

You are not accessing a virtual anything. Extension methods are static.

The function member that is invoked is determined by finding the most derived implementation of the function member with respect to B (instead of with respect to the run-time type of this, as would be usual in a non-base access). Thus, within an override of a virtual function member, a base-access can be used to invoke the inherited implementation of the function member.

So now we see what the purpose of a base access is: to enable a non-virtual dispatch to a virtual member that was overriden in the current type, or to call a base class member that was hidden by a new member in the current type. That is not what you are trying to use base for, and therefore you are doomed to failure. Stop using base off-label like this. Only use it when attempting to do a non-virtual dispatch to a virtual member, or get access to a hidden member.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
5

Eric Lippert has stated before that this was a somewhat conscious design decision here. You were never meant to use extension methods in a case where you have access to the implementation of your base class in the first place.

And if you think about it, you also dont need to do this here. Make a GetEnumerator property or method that is protected and use it! Basic object orientation; no need to torture linq here.

EDIT: It was pointed out that my previous suggestion did not work. So let me suggest just not implementing two different IEnumerable interfaces as this will cause a lot of headaches with foreach anyway.

I have come to believe that this implementation might be what you actually want:

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
{
}

You can then enumerate through Animal and their derivatives all the like

Community
  • 1
  • 1
srandppl
  • 571
  • 4
  • 14
  • You cannot cast `IEnumerator` to `IEnumerator` so this code wont compile. – Martin Liversage Jan 29 '16 at 12:20
  • You'd have to create a protected method in the base class that returns a IEnumerable for use by the derived class. – Erwin Mayer Jan 29 '16 at 12:28
  • I ended up just exposing `List Animals` in a protected fashion so that I can call `this.Animals.Cast().GetIterator()` from the `Turtle` class. – Erwin Mayer Jan 29 '16 at 14:28
  • May I ask why you need the XYZEnumerable classes at all? Why not just make IAnimal implement IEnumerable and be done with it. Then you can enumerate throught Animal and Turtle all you want via the interface – srandppl Jan 29 '16 at 14:39
  • I am in a rather complex case of interface composition, in my actual use case there are actually other interfaces involved. Everything could probably be refactored to avoid this, but for now I had to find a working solution. – Erwin Mayer Jan 29 '16 at 14:45
2

I will answer to that question:

Why will calling base.Cast() (or any LINQ method on the base element) in any method from the class Turtle fail to compile?

The reason of that exception is Cast and other such methods are extension methods. And extension methods are static.

For example, let's look at that:

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
    }
}

And as you know extension methods are a really nice syntactic sugar. They're not really added to the class, but the compiler makes it feel like they are. So, compiler will change that line of code to that one:

 Extensions.Method2(base); 

If you replace your code with that one the compiler will give more appropriate error message: Use of keyword base is not valid in this context.

As said in MSDN:

A base class access is permitted only in a constructor, an instance method, or an instance property accessor.

Farhad Jabiyev
  • 26,014
  • 8
  • 72
  • 98
  • Why wouldn't extension methods be callable using the base keyword? Are you aware of a workaround to access base elements (apart from the ugly `this.OfType().Cast().GetEnumerator();`)? – Erwin Mayer Jan 29 '16 at 11:47
  • You cannot call `base` from a static method to access the iterator and elements. – Erwin Mayer Jan 29 '16 at 11:49
  • @ErwinMayer Sorry, I was in a hurry. I will try to explain it more depply. – Farhad Jabiyev Jan 29 '16 at 11:50
  • Makes sense. If I have to use `this` instead, how can I then achieve what I want? – Erwin Mayer Jan 29 '16 at 12:09
  • @ErwinMayer The best way is to create instance method in base class, and then use it. Don't use the extension methods of the base class. – Farhad Jabiyev Jan 29 '16 at 12:19
  • I upvoted you because you provided a good explanation why `base` cannot be used for my purpose. I have good reasons to implement IEnumerable twice in my case so the first part of your answer is not very relevant and could be a reason why some people downvoted you. – Erwin Mayer Jan 29 '16 at 14:38
  • @ErwinMayer Thanks, actually I have added the first part 5-10 minutes ago. But, I don't know why my answer downvoted 2 times. May be I have answered only a part of your question. – Farhad Jabiyev Jan 29 '16 at 14:41
  • I think you also got downvoted when your answer contained mistakes (initially), some people do not assume you are going to fix them... – Erwin Mayer Jan 29 '16 at 14:43
  • 1
    @ErwinMayer May be :) Thanks. for trying to explain. Actually I have never came across this error message, so it took my time to make my answer better. No problem, I am happy that today I have learned more things. – Farhad Jabiyev Jan 29 '16 at 14:45
2

There are several issues with your approach. TurtleEnumerable implements both IEnumerable<Animal> and IEnumerable<Turtle>. To be able to use a TurtleEnumerable instance in a foreach loop you will have to cast it for the code to compile:

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

You are also using explicit interface implementations to hide the generic GetEnumerator() methods. You have to do that because you cannot do overload resolution on return type alone and the two generic GetEnumerator() methods only differ by return type.

However, this means that a TurtleEnumerable method cannot call the base GetEnumerator() method. The reason for this is that base does not behave like a variable of type "base". Instead it is a reserved word that only can be used to call base class methods. A corollary to this is that extension methods cannot be used with base. Also, you cannot cast base so explicit interface implementations on the base class are not callable through base.

However, you can cast this but because the generic GetEnumerator() on TurtleEnumerable hides the generic GetEnumerator() on AnimalEnumerable you will not be able to call into the base class so you will get a stack overflow because at some point the implementation of TurtleEnumerable.GetEnumerator() will call the same GetEnumerator.

To make your code compile you need to create a protected IEnumerator<Animal> GetEnumerator() method in your base class and create your own TurtleEnumerator class that wraps the base enumerator instance you can get by calling the protected method.

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();
    }

  }

}

All in all having a collection the implements both IEnumerable<Base> and IEnumerable<Derived> will get you into a lot of trouble. What are you trying to achieve by using this design?

Using a generic List<T> and contravariance you can do things like this:

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

You can also replace List<T> by your own generic collection type if that is required.

Martin Liversage
  • 104,481
  • 22
  • 209
  • 256
1

Why are you implementing the IEnumerable on the TurtleEnumerator class? Also, I don't think the accessibility on the AnimalEnumerable when you implemented the IEnumerable interface is correct.

Wouldn't it be implemented something like this:

    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....
        }
    }
Emma Middlebrook
  • 836
  • 1
  • 9
  • 19
  • I have good reasons to need to implement two interfaces which both implement IEnumerable. The example I used is only a simplification. However I ended up just exposing `List Animals` in a protected fashion so that I can call `this.Animals.Cast().GetIterator()` from the `Turtle` class. – Erwin Mayer Jan 29 '16 at 14:35