如果我只能定义一个 GetEnumerator,为什么要实现 IEnumerable(T)? [英] Why implement IEnumerable(T) if I can just define ONE GetEnumerator?

查看:14
本文介绍了如果我只能定义一个 GetEnumerator,为什么要实现 IEnumerable(T)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新:我感谢所有评论,这些评论基本上包括一致反对.虽然提出的每一个反对意见都是有效的,但我觉得棺材里的最终钉子是 Ani 的敏锐观察 最终,即使是这个想法表面上提供的one 微不足道好处——消除样板code -- 被这个想法本身需要它自己的样板代码这一事实否定了.

Update: I appreciate all of the comments, which have essentially comprised unanimous opposition. While every objection raised was valid, I feel that the ultimate nail in the coffin was Ani's astute observation that, ultimately, even the one miniscule benefit that this idea ostensibly offered -- the elimination of boilerplate code -- was negated by the fact that the idea itself would require its own boilerplate code.

所以,是的,相信我:这将是一个坏主意.

So yeah, consider me convinced: it would be a bad idea.

而且只是在某种程度上挽救了我的尊严:我可能会为了争论而夸大其词,但我从来没有真正接受过这个想法——只是好奇地想听听其他人对它的看法.诚实.

And just to sort of salvage my dignity somewhat: I might have played it up for argument's sake, but I was never really sold on this idea to begin with -- merely curious to hear what others had to say about it. Honest.

在你认为这个问题很荒谬之前,我请你考虑以下几点:

Before you dismiss this question as absurd, I ask you to consider the following:

  1. IEnumerable 继承自* IEnumerable,也就是说任何实现了IEnumerable的类型一般都必须实现 IEnumerable.GetEnumerator (显式)IEnumerable.GetEnumerator.这基本上相当于样板代码.
  2. 您可以在任何具有 GetEnumerator 方法的类型上foreach,只要该方法返回带有 MoveNext 的某种类型的对象方法和 Current 属性.因此,如果您的类型定义了 one 方法和签名 public IEnumerator;GetEnumerator(),使用 foreach 枚举它是合法的.
  3. 显然,有很多代码需要 IEnumerable 接口——例如,基本上所有的 LINQ 扩展方法.幸运的是,使用 C# 通过 yieldforeachIEnumerable 的类型是微不足道的代码>关键字.
  1. IEnumerable<T> inherits from* IEnumerable, which means that any type that implements IEnumerable<T> generally must implement both IEnumerable<T>.GetEnumerator and (explicitly) IEnumerable.GetEnumerator. This basically amounts to boilerplate code.
  2. You can foreach over any type that has a GetEnumerator method, as long as that method returns an object of some type with a MoveNext method and a Current property. So if your type defines one method with the signature public IEnumerator<T> GetEnumerator(), it's legal to enumerate over it using foreach.
  3. Clearly, there is a lot of code out there that requires the IEnumerable<T> interface -- for instance, basically all of the LINQ extension methods. Luckily, to go from a type that you can foreach on to an IEnumerable<T> is trivial using the automatic iterator generation that C# supplies via the yield keyword.

所以,把这一切放在一起,我有一个疯狂的想法:如果我只是定义自己的界面,看起来像这样:

So, putting this all together, I had this crazy idea: what if I just define my own interface that looks like this:

public interface IForEachable<T>
{
    IEnumerator<T> GetEnumerator();
}

然后每当我定义一个我想要可枚举的类型时,我都会实现this接口而不是IEnumerable,从而消除需要实现两个 GetEnumerator 方法(一个显式).例如:

Then whenever I define a type that I want to be enumerable, I implement this interface instead of IEnumerable<T>, eliminating the need to implement two GetEnumerator methods (one explicit). For example:

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接口的代码兼容,我可以定义一个从任何 IForEachableIEnumerable 的扩展方法,如下所示:

Finally, in order to make this type compatible with code that does expect the IEnumerable<T> interface, I can just define an extension method to go from any IForEachable<T> to an IEnumerable<T> like so:

public static class ForEachableExtensions
{
    public static IEnumerable<T> AsEnumerable<T>(this IForEachable<T> source)
    {
        foreach (T item in source)
        {
            yield return item;
        }
    }
}

在我看来,这样做使我能够设计在各种方面都可用的类型作为 IEnumerable 的实现,但没有那种烦人的显式 IEnumerable.GetEnumerator 在每一项中实施.

It seems to me that doing this enables me to design types that are usable in every way as implementations of IEnumerable<T>, but without that pesky explicit IEnumerable.GetEnumerator implementation in each one.

例如:

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

你们觉得这个主意怎么样?我是否错过了为什么这会是一个错误的原因?

What do you guys think of this idea? Am I missing some reason why this would be a mistake?

我意识到对于开始时没什么大不了的事情来说,这似乎是一个过于复杂的解决方案(我怀疑有人真的非常关心必须明确定义IEnumerable 接口);但它只是突然出现在我的脑海中,我没有看到这种方法会带来任何明显的问题.

I realize it probably seems like an overly complex solution to what isn't that big of a deal to start with (I doubt anybody really cares that much about having to explicitly define the IEnumerable interface); but it just popped into my head and I'm not seeing any obvious problems that this approach would pose.

一般来说,如果我能一次编写适量的代码,以免自己不得不多次编写少量代码,对我来说,值得.

In general, if I can write a moderate amount of code once to save myself the trouble of having to write a small amount of code lots of times, to me, it's worth it.

*这是要使用的正确术语吗?我总是犹豫说一个接口继承自"另一个接口,因为这似乎没有正确捕捉它们之间的关系.但也许它是正确的.

推荐答案

您是不是只是将样板文件移到其他地方 - 从在每个类上编写 IEnumerable.GetEnumerator 方法到调用您的 AsEnumerable 扩展每次需要 IEnumerable 时?通常,我希望可枚举类型用于查询的次数远多于写入的次数(恰好是一次).这意味着平均而言,这种模式将导致更多样板文件.

Aren't you just moving the boilerplate somewhere else - from writing the IEnumerable.GetEnumeratormethod on each class to calling your AsEnumerable extension every time an IEnumerable<T> is expected? Typically, I would expect an enumerable type to be used for querying far more times than it is written (which is exactly once). This would mean that this pattern will lead to more boilerplate, on average.

这篇关于如果我只能定义一个 GetEnumerator,为什么要实现 IEnumerable(T)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆