我尽可能尽可能地对接口进行编码,但是在集合方面我遇到了一些问题。例如,这里有一些我想使用的接口。
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);
}
答案 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对象。这样,您就可以完全控制实现,只展示设计合同(通过已实现的接口)