W
Wiktor Zychla [C# MVP]
The IEnumerable's documentation says:
"After an enumerator is created or after the Reset method is called, the
MoveNext method must be called to advance the enumerator to the first
element of the collection before reading the value of the Current property;
otherwise, Current is undefined.
Current also throws an exception if the last call to MoveNext returned
false, which indicates the end of the collection."
Note the "throws an exception" clause.
In case of the IEnumeable<T> this behavior is softened:
"Current is undefined under any of the following conditions:
The enumerator is positioned before the first element in the collection,
immediately after the enumerator is created. MoveNext must be called to
advance the enumerator to the first element of the collection before reading
the value of Current.
The last call to MoveNext returned false, which indicates the end of the
collection.
The enumerator is invalidated due to changes made in the collection, such as
adding, modifying, or deleting elements."
How does it work in real-life examples? What does it mean "is undefined"?
Let's see:
ArrayList a = new ArrayList();
a.Add( "ala ma kota" );
IEnumerator ea = a.GetEnumerator();
Console.WriteLine( ea.Current.ToString() ); // *
while ( ea.MoveNext() )
{
Console.WriteLine( ea.Current.ToString() );
}
Console.WriteLine( ea.Current.ToString() ); // **
Accessing the Current property BEFORE the enumeration starts (*) or just
after it finishes (**) ALWAYS throws an exception with detailed description
of the issue.
However, the generic interface behaves completely different:
List<string> ls = new List<string> { "ala ma kota", "qwe", "rrq" };
IEnumerator<string> es = ls.GetEnumerator();
Console.WriteLine( es.Current ); //*
while ( es.MoveNext() )
{
Console.WriteLine( es.Current );
}
Console.WriteLine( es.Current ); // **
This time there's NO EXCEPTION (the docs does not mention any exceptions,
that's why I said "softened") and instead the default(T) is returned.
In case of reference types, the default(T) is null so there's a chance to
catch this unwanted accessing of Current, however in case of value types,
the default(T) is 0!
Is it only myself who expects that accessing the Current property BEFORE the
enumeration starts or AFTER it finishes should ALWAYS throw an exception?
are there any significant reasons for such inconsistency between IEnumerable
and IEnumerable<T> implementations?
I kindly expect your comments on this issue.
Regards,
Wiktor Zychla
"After an enumerator is created or after the Reset method is called, the
MoveNext method must be called to advance the enumerator to the first
element of the collection before reading the value of the Current property;
otherwise, Current is undefined.
Current also throws an exception if the last call to MoveNext returned
false, which indicates the end of the collection."
Note the "throws an exception" clause.
In case of the IEnumeable<T> this behavior is softened:
"Current is undefined under any of the following conditions:
The enumerator is positioned before the first element in the collection,
immediately after the enumerator is created. MoveNext must be called to
advance the enumerator to the first element of the collection before reading
the value of Current.
The last call to MoveNext returned false, which indicates the end of the
collection.
The enumerator is invalidated due to changes made in the collection, such as
adding, modifying, or deleting elements."
How does it work in real-life examples? What does it mean "is undefined"?
Let's see:
ArrayList a = new ArrayList();
a.Add( "ala ma kota" );
IEnumerator ea = a.GetEnumerator();
Console.WriteLine( ea.Current.ToString() ); // *
while ( ea.MoveNext() )
{
Console.WriteLine( ea.Current.ToString() );
}
Console.WriteLine( ea.Current.ToString() ); // **
Accessing the Current property BEFORE the enumeration starts (*) or just
after it finishes (**) ALWAYS throws an exception with detailed description
of the issue.
However, the generic interface behaves completely different:
List<string> ls = new List<string> { "ala ma kota", "qwe", "rrq" };
IEnumerator<string> es = ls.GetEnumerator();
Console.WriteLine( es.Current ); //*
while ( es.MoveNext() )
{
Console.WriteLine( es.Current );
}
Console.WriteLine( es.Current ); // **
This time there's NO EXCEPTION (the docs does not mention any exceptions,
that's why I said "softened") and instead the default(T) is returned.
In case of reference types, the default(T) is null so there's a chance to
catch this unwanted accessing of Current, however in case of value types,
the default(T) is 0!
Is it only myself who expects that accessing the Current property BEFORE the
enumeration starts or AFTER it finishes should ALWAYS throw an exception?
are there any significant reasons for such inconsistency between IEnumerable
and IEnumerable<T> implementations?
I kindly expect your comments on this issue.
Regards,
Wiktor Zychla