开发者

Explicit implementation of interface's GetEnumerator causes stack overflow

开发者 https://www.devze.com 2022-12-10 04:02 出处:网络
I\'m doing my best to code against interfaces whenever possible, but I\'m having some issues when it comes to collections. For example, here are a couple interfaces I\'d like to use.

I'm doing my best to code against interfaces whenever possible, but I'm having some issues when it comes to collections. For example, here are a couple interfaces I'd like to use.

public interface IThing {}

public interface IThings : IEnumerable<IThing> {}

Here are the implementations. In order to implement IEnumerable<IThing> I need to explicitly implement IEnumerable<IThing>.GetEnumerator() in Things.

public class Thing : IThing {}

public class Things : List<Thing>, IThings
{
    IEnumerator<IThing> IEnumerable<IThing>.GetEnumerator()
    {
        // This calls itself over and over
        return this.Cast<IThing>().GetEnumerator();
    }
}

The problem is that the GetEnumerator implementation causes a stack overflow. It calls itself over and over again. I can't figure out why it'd decide to call that implementation of GetEnumerator instead of the implementation provided by the result of this.Cast<IThing>(). Any ideas what I'm doing wrong? I'm willing to bet it's something extr开发者_StackOverflow中文版emely silly...

Here's some simple test code for the above classes:

static void Enumerate(IThings things)
{
    foreach (IThing thing in things)
    {
        Console.WriteLine("You'll never get here.");
    }
}

static void Main()
{
    Things things = new Things();
    things.Add(new Thing());

    Enumerate(things);
}


Use this instead:

    public class Things : List<Thing>, IThings
    {
        IEnumerator<IThing> IEnumerable<IThing>.GetEnumerator()
        {
            foreach (Thing t in this)
            {
                yield return t;
            }
        }
    }

Or you could work with containment instead.


IEnumerator<IThing> IEnumerable<IThing>.GetEnumerator()
{
    // This calls itself over and over
    return this.Cast<IThing>().GetEnumerator();
}

It's a recursive call with no break condition, you're going to get a stack overflow, because it's going to very quickly fill up the stack.

You're casting to an interface, which does not do what you think it does. In the end you are just calling this.GetEnumerator(); which is the function you're already in, thus recursing. Perhaps you mean base.GetEnumerator();


Generally, you would probably want to decouple the implementation of the Things collection from the implementation of the Thing object. The Things class is capable of working with any implementation of IThing, not just the Thing class.

Specifically:

public class Things : List<IThing>, IThings
{
}

In this case, you don't have to override the default implementation of GetEnumerator(), the base implementation is already typed correctly for you. This will avoid the overflow that you're currently experiencing and satisfies the test case you provided.


This is a nice example of the need for the language and runtime to understand covariance and contravariance.

In C# 4, you can just use

IEnumerator<IThing> IEnumerable<IThing>.GetEnumerator()
{
    return base.GetEnumerator();
}


Seems pretty obvious that this.Cast<IThing>() returns an IEnumerable. Or, at least it does without the implementation of Cast.


I don't know if a question qualifies as an answer but bear with me Im a consultant ;P Why do you wanna implement the GetEnumerator()?

You're deriving from List if you change that to List you get the GetEnumerator for free.

Be aware too that deriving from List is generally a bad idea. you should consider deriving from IEnumerable<()IThings> (as you're already doing which is atm superflurous since List also implements that interface) or if you need the List interface and not only the IEnumerable implement IList and keep a private List object. That way you get full control about the implementation and only expose a design contract (through the implemented interfaces)

0

精彩评论

暂无评论...
验证码 换一张
取 消