更新:我感谢所有评论,这些评论基本上包括一致反对。虽然提出的每一个反对意见都是有效的,但我认为棺材上的最终钉子是Ani 的敏锐观察,最终,即使这个想法表面上提供的一个 微不足道的好处——消除样板代码——也被以下事实所否定:想法本身将需要自己的样板代码。
所以,是的,请相信我:这将是一个坏主意。
只是为了在某种程度上挽救我的尊严:我可能为了争论而夸大了它,但我从来没有真正相信过这个想法——只是好奇地想听听其他人对此的看法。诚实。
在你认为这个问题很荒谬之前,我要求你考虑以下几点:
IEnumerable<T>
继承自 *IEnumerable
,这意味着实现的任何类型IEnumerable<T>
通常必须同时实现IEnumerable<T>.GetEnumerator
and (explicitly)IEnumerable.GetEnumerator
。这基本上相当于样板代码。- 您可以
foreach
覆盖任何具有GetEnumerator
方法的类型,只要该方法返回具有MoveNext
方法和Current
属性的某种类型的对象。因此,如果您的类型定义了一个带有签名的方法public IEnumerator<T> GetEnumerator()
,那么使用 . 枚举它是合法的foreach
。 - 显然,有很多代码需要该
IEnumerable<T>
接口——例如,基本上所有的 LINQ 扩展方法。幸运的是,使用 C# 通过关键字提供的自动迭代器生成,从一个可以转换为一个类型foreach
是微不足道的。IEnumerable<T>
yield
所以,把这一切放在一起,我有一个疯狂的想法:如果我只是定义我自己的界面,看起来像这样:
public interface IForEachable<T>
{
IEnumerator<T> GetEnumerator();
}
然后,每当我定义一个我想要可枚举的类型时,我都会实现这个接口而不是IEnumerable<T>
,从而无需实现两个GetEnumerator
方法(一个显式)。例如:
class NaturalNumbers : IForEachable<int>
{
public IEnumerator<int> GetEnumerator()
{
int i = 1;
while (i < int.MaxValue)
{
yield return (i++);
}
}
// Notice how I don't have to define a method like
// IEnumerator IEnumerable.GetEnumerator().
}
最后,为了使这种类型与需要接口的代码兼容,我IEnumerable<T>
可以定义一个扩展方法来从 anyIForEachable<T>
变为IEnumerable<T>
like:
public static class ForEachableExtensions
{
public static IEnumerable<T> AsEnumerable<T>(this IForEachable<T> source)
{
foreach (T item in source)
{
yield return item;
}
}
}
在我看来,这样做使我能够设计出在各个方面都可以IEnumerable<T>
作为IEnumerable.GetEnumerator
.
例如:
var numbers = new NaturalNumbers();
// I can foreach myself...
foreach (int x in numbers)
{
if (x > 100)
break;
if (x % 2 != 0)
continue;
Console.WriteLine(x);
}
// Or I can treat this object as an IEnumerable<T> implementation
// if I want to...
var evenNumbers = from x in numbers.AsEnumerable()
where x % 2 == 0
select x;
foreach (int x in evenNumbers.TakeWhile(i => i <= 100))
{
Console.WriteLine(x);
}
大家觉得这个想法怎么样?我错过了为什么这会是一个错误的一些原因吗?
我意识到这可能看起来像是一个过于复杂的解决方案,而不是一开始就没什么大不了的事情(我怀疑是否有人真的那么关心必须显式定义IEnumerable
接口);但它只是突然出现在我的脑海中,我没有看到这种方法会带来任何明显的问题。
一般来说,如果我可以一次编写适量的代码,省去多次编写少量代码的麻烦,对我来说,这是值得的。
*这是正确的术语吗?我总是犹豫要不要说一个接口“继承自”另一个接口,因为这似乎不能正确捕捉它们之间的关系。但也许它是正确的。