Why implement IEnumerable(T) if I can just define ONE GetEnumerator?

故事扮演 提交于 2019-11-29 13:13:42

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.

You're missing one huge thing -

If you implement your own interface instead of IEnumerable<T>, your class will not work with the framework methods expecting IEnumerable<T> - mainly, you will be completely unable to use LINQ, or use your class to construct a List<T>, or many other useful abstractions.

You can accomplish this, as you mention, via a separate extension method - however, this comes at a cost. By using an extension method to convert to an IEnumerable<T>, you're adding another level of abstraction required in order to use your class (which you'll do FAR more often than authoring the class), and you decrease performance (your extension method will, in effect, generate a new class implementation internally, which is really unnecessary). Most importantly, any other user of your class (or you later) will have to learn a new API that accomplishes nothing - you're making your class more difficult to use by not using standard interfaces, since it violates the user's expectations.

You're right: it does seem an overly complex solution to a pretty easy problem.

It also introduces an extra level of indirection for every step of the iteration. Probably not a performance problem, but still somewhat unnecessary when I don't think you're really gaining anything very significant.

Also, although your extension method lets you convert any IForEachable<T> into an IEnumerable<T>, it means your type itself won't satisfy a generic constraint like this:

public void Foo<T>(T collection) where T : IEnumerable

or the like. Basically, by having to perform your conversion, you're losing the ability to treat a single object as both an implementation of IEnumerable<T> and the real concrete type.

Also, by not implementing IEnumerable, you're counting yourself out of collection initializers. (I sometimes implement IEnumerable explicitly just to opt into collection initialization, throwing an exception from GetEnumerator().)

Oh, and you've also introduced an extra bit of infrastructure which is unfamiliar to everyone else in the world, compared with the vast hordes of C# developers who already know about IEnumerable<T>.

In our codebase there's probably more than 100 instances of this exact snippet:

IEnumerator IEnumerable.GetEnumerator()
{
    return this.GetEnumerator();
}

And I am really OK with that. It's a tiny price to pay for full compatibility with every other piece of .NET code ever written ;)

Your proposed solution essentially requires every consumer/caller of your new interface to also remember to call a special extension method on it before it is useful.

It is so much easier to use IEnumerable for reflection. Trying to invoke generic interfaces via reflection is such a pain. The penalty of boxing via IEnumerable is lost by the overhead of reflection itself so why bother using a generic interface? As for an example, serialization comes to mind.

I think everyone has already mentioned the technical reasons not to do this, so I'll add this into the mix: requiring your user to call AsEnumerable() on your collection to be able to use enumerable extensions would violate the principle of least surprise.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!