[LINQ via C# series]
According to Wikipedia:
Iterator pattern is a design pattern in which iterators are used to access the elements of an aggregate object sequentially without exposing its underlying representation. An Iterator object encapsulates the internal structure of how the iteration occurs.
In C#, iterator pattern is implemented via the IEnumerable / IEnumerable<T> interfaces. According to the previous post, a LINQ to Objects query method works on IEnumerable<T> instead of IEnumerable. While working, the query accesses the elements of the IEnumerable<T> sequentially.
To take a deeper look at the iterator pattern, check out this paper from OUCL.
IEnumerable and IEnumerable<T>
To implements iterator pattern, IEnumerable is introduced into .NET from the beginning since .NET does not have generics at that time. IEnumerable is used to get an iterator represented by IEnumerator:
namespace System.Collections
{
// Represents a collection which can be iterated.
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
// Represents an iterator which is used to iterate that collection.
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
}
.NET 2.0 introduced generics so IEnumerable<T> is defined:
namespace System.Collections.Generic
{
// T represents the item type.
public interface IEnumerable<T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
// T represents the item type.
public interface IEnumerator<T> : IDisposable, IEnumerator
{
T Current { get; }
}
}
And .NET 4.0 introduces covariance and contravariance for generic delegates and generic interfaces. So IEnumerable<T> becomes:
namespace System.Collections.Generic
{
// T is covariant.
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
// T is covariant.
public interface IEnumerator<out T> : IDisposable, IEnumerator
{
T Current { get; }
}
}
The meaning of out keyword is explained in another post Understanding C# Covariance And Contravariance (2) Interfaces.
IEnumerable<T> is more strong-typed than IEnumerable. The key difference is the Current property:
- IEnumerable gnerates an IEnumerator, IEnumerator’s Current property’s return type is object, usually a cast is required;
- IEnumerable<T> gnerates an IEnumerator<T>, IEnumerator<T>’s Current property’s return type is T, no cast is needed.
foreach syntactic sugar
In C#, the foreach statement iterates items in a collection that implements IEnumerable or IEnumerable<T>. It is just a syntactic sugar.
To foreach an IEnumerable:
IEnumerable collection = GetData();
foreach (T item in collection)
{
Action(item);
}
will be compiled into:
IEnumerable collection = GetData();
IEnumerator iterator = collection.GetEnumerator();
try
{
while (iterator.MoveNext())
{
T item = (T)iterator.Current; // Cast is required.
Action(item);
}
}
finally
{
// If iterator also implements IDisposable, dispose it.
IDisposable disposable = iterator as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
To foreach an IEnumerable<T>:
IEnumerable<T> collection = GetData();
foreach (T item in collection)
{
Action(item);
}
Generally speaking, the first foreach on IEnumerable is compiled into:
will be compiled into:
IEnumerable<T> collection = GetData();
IEnumerator<T> iterator = collection.GetEnumerator();
try
{
while (iterator.MoveNext())
{
T item = iterator.Current; // Cast is not needed.
Action(item);
}
}
finally
{
// IEnumerable<T> implements IDisposable.
iterator.Dispose();
}
Since IEnumerator<T> implements IDisposable, the using syntactical sugar can be used to shorten the code a little bit:
IEnumerable<T> collection = GetData();
using (IEnumerator<T> iterator = collection.GetEnumerator())
{
while (iterator.MoveNext())
{
T item = iterator.Current; // Cast is not needed.
Action(item);
}
}
Because IEnumerable<T> is more strong-typed, it is always preferred to use.
Special iterations
In .NET, array is very special. This is the definition:
namespace System
{
public abstract class Array : ICloneable, IList, ICollection, IEnumerable,
IStructuralComparable, IStructuralEquatable
{
// ...
}
}
First, it implements only IEnumerable. But for a concrete array T[], CLR automatically make it inherit Array and implement IEnumerable<T>, ICollection<T>, and IList<T>, as long as T[] is:
- single dimensional;
- zero–lower bound.
Second, foreaching an array:
T[] array = GetData();
foreach (T item in array)
{
Action(item);
}
will be compiled into a for loop to improve the performance:
T[] array = GetData();
for (int index = 0; index < array.Length; index++)
{
Action(array[index]);
}
String is also special. foreaching chars in a string will also be compiled into a for loop, checking the string’s Length.
Prefer IEnumerable<T> than IEnumerable
Again, IEnumerable<T> is strong-typed and should always be preferred. All LINQ to Objects query methods are designed for IEnumerable<T>.
However, for obvious historical reason, many types in .NET implements IEnumerable.
The following extension methods can be used to query types implementing a specified interface:
public static IEnumerable<Type> Implement(this IEnumerable<Type> source, Type @interface)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (@interface == null)
{
throw new ArgumentNullException("@interface");
}
if (!@interface.IsInterface)
{
throw new ArgumentOutOfRangeException("@interface");
}
if (@interface.IsGenericType)
{
return source.Where(
type => type != @interface && type.GetInterfaces().Any(
item => item.IsGenericType && item.GetGenericTypeDefinition() == @interface));
}
return source.Where(
type => type != @interface && @interface.IsAssignableFrom(type));
}
In mscorlib.dll of .NET 4.0:
IEnumerable<Type> nonGeneric = Assembly.Load("mscorlib").GetExportedTypes()
.Implement(typeof(IEnumerable));
foreach (Type type in nonGeneric) // 48 types implements IEnumerable.
{
Console.WriteLine(type.FullName);
}
IEnumerable<Type> generic = Assembly.Load("mscorlib").GetExportedTypes()
.Implement(typeof(IEnumerable<>));
foreach (Type type in generic) // 16 types implements IEnumerable<T>.
{
Console.WriteLine(type.FullName);
}
LINQ query on IEnumerable
An IEnumerable extension method Cast() is provided. After converting an IEnumerable to a IEnumerable<T>, all LINQ to Objects query methods can be applied:
IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
For example, ProfileInfoCollection implements IEnumerable:
namespace System.Web.Profile
{
public sealed class ProfileInfoCollection : ICollection, IEnumerable
{
// ...
}
}
To apply LINQ query, just cast it:
ProfileInfoCollection collection = GetData();
IEnumerable<string> names = collection.Cast<ProfileInfo>()
.Where(profile => !profile.IsAnonymous)
.Select(profile => profile.UserName);