问题
I use the following code to enable myClass to use foreach. But I am rather new to programming and have some difficulty in understanding the following code. I described my problems in the comments. I would be grateful for providing some information.
public class MyClass : IEnumerable<string>
{
//1) What is IEnumerator for?
// Whats the difference between IEnumerator and IEnumerable
public IEnumerator<string> GetEnumerator()
{
yield return "first";
yield return "second";
}
//2) What is it for? It just calls above method
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
//3) Lastly what benefits I have from implementing genetic interface
//IEnumerable<string> instead of just IEnumerable
回答1:
What is the difference between IEnumerator and IEnumerable?
Jason's answer is good but I thought I'd just add how I think about this. Imagine you have a sequence:
1, 1, 2, 3, 5, 8, 13, ...
Now imagine you have an arrow pointing at some position of that sequence:
1, 1, 2, 3, 5, 8, 13, ...
^
An "arrow" is an object that can do two things. First, it can give you the thing it is pointing at. Second, it can make itself point at the next thing.
IEnumerator is an arrow. It has a property, Current, that gives you the thing it is pointing at. It has a method, MoveNext() that makes itself point at the next thing.
How do you get an arrow in the first place? You need an arrow factory. You ask the factory for an arrow, and it gives you an arrow that points to the first element in the sequence.
IEnumerable is an arrow factory. It has a method, GetEnumerator, that gives you an arrow to the first element of the sequence.
A nice property of this scheme is that you can have multiple arrows pointing to different places in the same sequence.
what are the benefits of implementing generic interface IEnumerable instead of just IEnumerable?
Suppose the sequence is of integers. If you implement IEnumerable
then when you say
foreach(int x in mysequence)
what that will actually do is convert the int in the sequence to object, boxing the integer, and then immediately unbox the object back to integer, adding a completely unnecessary memory allocation to every single operation. If the compiler knows that the sequence is of integers then it can skip the unnecessary boxing operation.
Suppose the sequence is of strings. If you implement IEnumerable<string>
then you can say:
string first = mysequence.First();
If you don't, then you have to say
string first = (string)mysequence.First();
which is unnecessary and error-prone. Rather than instruct the compiler via a cast that the type is string, you can simply guarantee that the type is string by using the type system.
回答2:
1) What is
IEnumerator
for? Whats the difference betweenIEnumerator
andIEnumerable
?
IEnumerator
is an interface that represents methods that let you enumerate a sequence. The difference between IEnumerator
and IEnumerable
is that the former represents the contract for objects that let you enumerate a sequence, and the latter represents the contract for objects that are a sequence that can be enumerated over.
public IEnumerator<string> GetEnumerator() {
yield return "first";
yield return "second";
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
2) What is it for? It just calls above method.
The former represents an implementation of the method GetEnumerator
on the contract IEnumerable<string>
. The latter represents an explicit implementation of the method GetEnumerator
on the contract IEnumerable
. The issue is that both contracts have a method named GetEnumerator
but with different return types so that a method can't simultaneously satisfy both contracts (any class implementing IEnumerable<T>
must also implement IEnumerable
as IEnumerable<T> : IEnumerable
). The latter invokes the implementation of IEnumerable<string>.GetEnumerator
as that is a sensible implementation that returns an IEnumerator
as IEnumerator<string> : IEnumerator
.
3) Lastly what benefits I have from implementing generic interface
IEnumerable<string>
instead of justIEnumerable
?
Strong typing. You know that the elements in a sequence IEnumerable<string>
are all instances of String
whereas you don't know that for IEnumerable
and could end up trying to cast an element of the latter to an instance of String
when it can not be.
回答3:
This is the declaration for IEnumerable<T>
:
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
You have no choice, you have to implement the non-generic IEnumerable interface as well. You'll get a compile error if you omit the IEnumerable.GetEnumerator() implementation. This is baggage left over from the .NET 1.x days where generics weren't available yet and the transition period for .NET 2.0 where it needed to support interop with .NET 1.x assemblies. We're stuck with it.
回答4:
1) What is IEnumerator for?
IEnumerator is the real working part with the MoveNext() and Current members.
Whats the difference between IEnumerator and IEnumerable
IEnumerable is the interface for a collection to signal that it has an GetEnumerator().
2) [non-generic GetEnumerator] What is it for? It just calls above method
The non-generic method is just there for backward compatibility. Note that it is moved 'out of sight' as much as possible by use of the explicit implementation. Implement it because you must and then forget about it.
3) Lastly what benefits I have from implementing genetic interface IEnumerable instead of just IEnumerable
When used with foreach
the adavantage is small, as foreach will type-cast the looping variable. It will let you use var
in the foreach:
foreach (var s in myClassInstance) // needs `IEnumerable<string>`
foreach (string s in myClassInstance) // works with `IEnumerable` as well
But with IEnumerable<string>
you also have a type-safe interface to other areas, most notably LINQ:
MyClass mc = new MyClass ();
string s = mc.FirstOrDefault();
回答5:
GetEnumerator
has been around since before generics were introduced to the language, so as to not break pre-existing code, the default method calls through to the generic implementation.
With a generic enumeration, the consumer of your class doesn't need to cast each item to string when iterating.
回答6:
Regarding the 3rd question (why use generics?) here are some answers:
Should we use Generic Collection to improve safety and performance?
In short, use generic collections every time you can.
回答7:
To understand the difference you need to understand its use. Actually, they represent an Iterator design pattern which is one of the design patterns used by object-oriented software developers.
The Iterator pattern provides a way of accessing elements of a collection sequentially, without knowing how the collection is structured.
In the traditional design pattern approach, the iterator pattern has an Aggregate interface for creating an Iterator object and the Iterator interface for traversing an Aggregate object.
Now, IEnumerable acts as an Aggregate interface that guarantees to return an iterator. Iterators (also known as Enumerators) are responsible for producing the next element in a sequence defined by certain criteria. IEnumerator is an interface that defines this functionality. You can learn more about this pattern in my blog post Iterator Pattern C#
来源:https://stackoverflow.com/questions/4548464/question-regarding-ienumerable-and-ienumerator