接口的GetEnumerator的显式实现导致堆栈溢出

时间:2009-11-05 18:12:18

标签: c#

我尽可能尽可能地对接口进行编码,但是在集合方面我遇到了一些问题。例如,这里有一些我想使用的接口。

public interface IThing {}

public interface IThings : IEnumerable<IThing> {}

以下是实施方案。为了实现IEnumerable&lt; IThing&gt;我需要在Things中明确地实现IEnumerable&lt; IThing&gt; .GetEnumerator()。

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

问题是GetEnumerator实现导致堆栈溢出。它一遍又一遍地呼唤着自己。我无法弄清楚为什么它决定调用GetEnumerator的实现而不是this.Cast&lt; IThing&gt;()的结果提供的实现。我有什么想法我做错了吗?我愿意打赌这是非常愚蠢的......

以下是上述类的一些简单测试代码:

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

6 个答案:

答案 0 :(得分:3)

请改用:

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

或者你可以改为使用遏制措施。

答案 1 :(得分:1)

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

这是一个没有中断条件的递归调用,你会得到一个堆栈溢出,因为它会很快填满堆栈。

你正在投射到一个界面,这不符合你的想法。最后你只是调用this.GetEnumerator();这是你已经在的功能,因此递归。也许你的意思是base.GetEnumerator();

答案 2 :(得分:1)

通常,您可能希望将Things集合的实现与Thing对象的实现分离。 Things类能够处理任何IThing实现,而不仅仅是Thing类。

具体做法是:

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

在这种情况下,您不必覆盖GetEnumerator()的默认实现,已经为您正确键入了基本实现。这样可以避免您目前遇到的溢出并满足您提供的测试用例。

答案 3 :(得分:1)

这是语言和运行时需要理解协方差和逆变的一个很好的例子。

在C#4中,您可以使用

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

答案 4 :(得分:0)

很明显,this.Cast<IThing>()返回IEnumerable。或者,至少它没有实现Cast

答案 5 :(得分:0)

我不知道一个问题是否有资格作为答案但是我可以担任顾问; P为什么要实现GetEnumerator()?

如果您将其更改为List,那么您将从List派生,您可以免费获得GetEnumerator。

请注意,从List中获取通常是一个坏主意。你应该考虑从IEnumerable&lt;()IThings&gt;派生出来(因为你已经在做什么,因为List也实现了那个接口,所以它是超级硬盘)或者你需要List接口而不仅仅是IEnumerable实现IList并保留一个私有的List对象。这样,您就可以完全控制实现,只展示设计合同(通过已实现的接口)