Reputation: 1019
To implement collection, I have to complete GeEnumerator(). But the return type of this function is IEnumerator. How can it be done ? For example..
class MyList : IEnumerable, IEnumerator
{
private int[] array;
int positino = -1;
public IEnumerator GetEnumerator()
{
for(int i = 0 ; i < array.Length; ++i)
{
yield return array[i];
}
}
}
array[i] is integer not IEnumerator type. How can this function return integer?
Upvotes: 1
Views: 3984
Reputation: 113382
How can this function return integer
It doesn't, it yield return
s an integer. This means that it can be compiled into a method that returns IEnumerable
, IEnumerable<int>
, IEnumerator
or IEnumerator<int>
depending on which your method says it returns.
The yield
keyword is a convenience that allows for the creation of an enumerator with the appropriate MoveNext()
, Current
, and Dispose
, and if it's an enumerable then the appropriate GetEnumerator
too.
Looking at your method:
public IEnumerator GetEnumerator()
{
for(int i = 0 ; i < array.Length; ++i)
{
yield return array[i];
}
}
Then this is something that compiles, though to really work it needs more to actually assign something to array
.
When we compile it, it has the same result as if we'd written:
private class En : IEnumerator<object>
{
private object _current;
private int _state;
public MyList _this;
public int _index;
public bool MoveNext()
{
int state = _state;
switch(state)
{
case 0:
_state = -1;
_index = 0;
break;
case 1:
_state = -1;
if(++_index >= _this.array.Length)
return false;
break;
default:
return false;
}
_current = _this.array[_index];
_state = 1;
return true;
}
public object Current
{
get { return _current; }
}
public void Reset()
{
throw new NotSupportedException();
}
public void Dispose()
{
}
object IEnumerator.Current
{
get { return _current; }
}
public En(int state)
{
_state = state;
}
}
public IEnumerator GetEnumerator()
{
var en = new En(0);
en._this = this;
return en;
}
The main difference is that En
and its fields would all have names that aren't valid C# names but are valid .NET names, so it can't clash with any names a programmer used.
This has a bit more to it than it needs. It implements IEnumerator<object>
as well as IEnumerator
, but then that means the compiler can just have logic for IEnumerable<T>
and use object
for the T
for the non-generic type rather than having separate logic. _state
is more complicated than necessary, but if the yield
-using method was more complicated than a single loop, then different values for _state
would allow it to keep track of which part of that yield
-using method it was corresponding with.
In all it's done a good job of implementing IEnumerator
. It's a bit larger than if you were to hand-code one (and obviously larger than if you'd just used return array.GetEnumerator();
), but not a lot larger, but conversely the yield
-using method is a lot shorter and often these methods are simpler. This is even more so in cases where e.g. you have a using
block in the yield
-using method, which gets turned into the appropriate clean-up in the enumerator's Dispose()
.
Upvotes: 2
Reputation: 111940
Note that in this specific case you can "cheat" and not really implement GetEnumerator()
, using the array implementation:
public class MyList : IEnumerable<int>
{
public MyList()
{
array = new[] { 1, 2, 3, 4, 5, 6, 7 };
}
private int[] array;
public IEnumerator<int> GetEnumerator()
{
return ((IEnumerable<int>)array).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Upvotes: 1
Reputation: 1056
Use the generics and IEnumerable int
public class MyList : IEnumerable<int>
{
public MyList()
{
array = new [] { 1, 2, 3, 4, 5, 6, 7};
}
private int[] array;
int positino = -1;
public IEnumerator<int> GetEnumerator()
{
for(int i = 0 ; i < array.Length; ++i)
{
yield return array[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
Upvotes: 2