Point of need of custom Enumerators
When reading a post some points were given without example :
To implement IEnumerable / IEnumerable, you must provide an enumerator :
•If the class is "wrapping" another collection, by returning the wrapped collection's enumerator.
•Via an iterator using yield return.
•By instantiating your own IEnumerator/IEnumerator implementation
(My baby mind interprets it as)
(Point 1)
If the class is "wrapping" another collection, by returning the
wrapped collection's enumerator.
Will it mean ..
class StringCollections
{
//A class is wrapping another collection
string[] names=new string {“Jon Skeet”,”Hamish Smith”,
”Marc Gravell”,”Jrista”,”Joren”};
//by returning the wrapped collection’s enumerator
public IEnumerator GetEnumerator( )
{
// What should I return here ?
//like the following ?
yield return names[0];
yield return names[1];
yield return names[2];
....
(or)
foreach(string str in names)
{
yield return str;
}
}
}
(Point 2)
•Via an iterator using yield return.(This point was well explained
by Marc Gravell)
Point 3
By instantiating your own IEnumerator/IEnumerato开发者_运维百科r<T> implementation*
What does point 3 represent here?,since no example,i did not get that one. Does it mean,i can build custom enumerator ..(right?).My question here is when prebuild enumerator/enumerators are sufficient (as a beginner i should not blindly confirm this) for iterations why should i look after the custom one ?Nice example will clarify my doubt.
Thanks for reading this long winded story and kind response.
(point 1) You can chain the call of GetEnumerator to other collection enumerator:
public class PrimeNumbers : IEnumerable
{
public IEnumerator GetEnumerator()
{
var primes = new List<int> { 2, 3, 5, 7, 11 };
return primes.GetEnumerator();
}
}
(point 2) Similar to example in your code
public IEnumerator GetEnumerator()
{
var primes = new List<int> { 2, 3, 5, 7, 11 };
foreach (var number in primes)
{
yield return number;
}
}
Or place enumerator with logic:
public class PrimeNumbers : IEnumerable
{
public IEnumerator GetEnumerator()
{
for(int i=2; ;i++)
{
if(IsPrime(i))
{
yield return i;
}
}
}
}
(Point 3) There are not many cases where you want to implement your own Enumerator, but for example you can look for an infinite set of values, e.g. prime numbers:
public class PrimeNumbers : IEnumerable
{
public IEnumerator GetEnumerator()
{
return new MyEnumerator();
}
}
public class MyEnumerator : IEnumerator
{
private int lastPrimeNumber = 1;
public bool MoveNext()
{
lastPrimeNumber = /* some logic that find the next prime */;
return true; // There is always next prime
}
public void Reset()
{
lastPrimeNumber = 1;
}
public object Current
{
get { return lastPrimeNumber; }
}
}
A usage example could be:
public void PrintAllPrimes()
{
var numbers = new PrimeNumbers();
// This will never end but it'll print all primes until my PC crash
foreach (var number in numbers)
{
Console.WriteLine(number);
}
}
Pros & Cons I can think of:
- Point 1: It's the simplest way to enumerate items but it requires knowing all items in advance
- Point 2: It's readable when there's some logic in the enumeration, it's also lazy so there is no need to compute an item until it's actually requested
- Point 3: It's simplest to reuse but it is less readable (calculate item in MoveNext but actually return from Current property).
Here's an example: (NOTE: This is simplified, not thread safe example)
public class PersonCollection : IEnumerable
{
private ArrayList alPers = new ArrayList();
public IEnumerator GetEnumerator() { return new myTypeEnumerator(this); }
public class myTypeEnumerator : IEnumerator
{
int nIndex;
PersonCollection pers;
private int count { get { return pers.alPers.Count; } }
public myTypeEnumerator(PersonCollection myTypes)
{ pers = myTypes; nIndex = -1; }
public bool MoveNext() { return nIndex <= count && ++nIndex < count; }
// MovePrev() not strictly required
public bool MovePrev() { return (nIndex > -1 && --nIndex > 0 ); }
public object Current { get { return (pers[nIndex]); } }
public void Reset() { nIndex = -1; }
}
}
EDIT: to fix issue raised by @Joren below related to Move Previous taking index value below -1. When called by framework as part of foreach implementation, MoveNext() would not need this fix because in that case if MoveNext() returns false, enumerator instance will terminate. However, if client manually calls MoveNext(), enumerator would not terminate, so it also needs the fix.
Also NOTE: how you implement the details inside this thing is up to you, and will be dependant on how it internally manages state. For example if the iunternal "bucket" that holds the references was a linked list, instead of an ArrayList, then the MoveNext() might be implemented by just changing a Current field to point to the old current's nextItem property...
What you said for 1 would actually be for 2.
For 1, it'd be more like
public IEnumerator GetEnumerator() {
return names.GetEnumerator();
}
For point 3, it'd mean to make GetEnumerator do some real work: Implementing, by hand, an Enumerator for the collection you defined.
"If the class is "wrapping" another collection, by returning thewrapped collection's enumerator" means:
class StringCollections{
//A class is wrapping another collection
string[] names=new string {“Jon Skeet”,”Hamish Smith”,
”Marc Gravell”,”Jrista”,”Joren”};
public IEnumerator GetEnumerator() { return names.GetEnumerator(); }
}
精彩评论