|
This tutorial shows how to implement a collection class that can be used with the foreach statement. Further ReadingTutorialThe foreach statement is a convenient way to iterate over the elements of an array. It can also enumerate the elements of a collection, provided that the collection class has implemented the System.Collections.IEnumerator and System.Collections.IEnumerable interfaces. Example 1The following code sample illustrates how to write a collection class that can be used with foreach. The class is a string tokenizer, similar to the C run-time function strtok. // tokens.cs
using System;
// The System.Collections namespace is made available:
using System.Collections;
// Declare the Tokens class:
public class Tokens : IEnumerable
{
private string[] elements;
Tokens(string source, char[] delimiters)
{
// Parse the string into tokens:
elements = source.Split(delimiters);
}
// IEnumerable Interface Implementation:
// Declaration of the GetEnumerator() method
// required by IEnumerable
public IEnumerator GetEnumerator()
{
return new TokenEnumerator(this);
}
// Inner class implements IEnumerator interface:
private class TokenEnumerator : IEnumerator
{
private int position = -1;
private Tokens t;
public TokenEnumerator(Tokens t)
{
this.t = t;
}
// Declare the MoveNext method required by IEnumerator:
public bool MoveNext()
{
if (position < t.elements.Length - 1)
{
position++;
return true;
}
else
{
return false;
}
}
// Declare the Reset method required by IEnumerator:
public void Reset()
{
position = -1;
}
// Declare the Current property required by IEnumerator:
public object Current
{
get
{
return t.elements[position];
}
}
}
// Test Tokens, TokenEnumerator
static void Main()
{
// Testing Tokens by breaking the string into tokens:
Tokens f = new Tokens("This is a well-done program.",
new char[] {' ','-'});
foreach (string item in f)
{
Console.WriteLine(item);
}
}
}
OutputThis is a well done program. Code DiscussionIn the preceding example, the following code is used to Tokens f = new Tokens("This is a well-done program.",
new char[] {' ','-'});
foreach (string item in f)
{
Console.WriteLine(item);
}
Notice that, internally, In C#, it is not strictly necessary for a collection class to inherit from IEnumerable and IEnumerator in order to be compatible with foreach; as long as the class has the required For example, starting with the sample code above, change the following lines: public class Tokens // no longer inherits from IEnumerable public TokenEnumerator GetEnumerator() // doesn't return an IEnumerator public class TokenEnumerator // no longer inherits from IEnumerator public string Current // type-safe: returns string, not object Now, because foreach (int item in f) // Error: cannot convert string to int The disadvantage of omitting IEnumerable and IEnumerator is that the collection class is no longer interoperable with the foreach statements (or equivalents) of other common language runtime-compatible languages. You can have the best of both worlds - type-safety within C# and interoperability with other common language runtime-compatible languages - by inheriting from IEnumerable and IEnumerator and using explicit interface implementation, as demonstrated in the following example. Example 2This sample is equivalent in function to Example 1, but it provides additional type-safety in C# while maintaining interoperability with other languages. // tokens2.cs
using System;
using System.Collections;
public class Tokens: IEnumerable
{
private string[] elements;
Tokens(string source, char[] delimiters)
{
elements = source.Split(delimiters);
}
// IEnumerable Interface Implementation:
public TokenEnumerator GetEnumerator() // non-IEnumerable version
{
return new TokenEnumerator(this);
}
IEnumerator IEnumerable.GetEnumerator() // IEnumerable version
{
return (IEnumerator) new TokenEnumerator(this);
}
// Inner class implements IEnumerator interface:
public class TokenEnumerator: IEnumerator
{
private int position = -1;
private Tokens t;
public TokenEnumerator(Tokens t)
{
this.t = t;
}
public bool MoveNext()
{
if (position < t.elements.Length - 1)
{
position++;
return true;
}
else
{
return false;
}
}
public void Reset()
{
position = -1;
}
public string Current // non-IEnumerator version: type-safe
{
get
{
return t.elements[position];
}
}
object IEnumerator.Current // IEnumerator version: returns object
{
get
{
return t.elements[position];
}
}
}
// Test Tokens, TokenEnumerator
static void Main()
{
Tokens f = new Tokens("This is a well-done program.",
new char [] {' ','-'});
foreach (string item in f) // try changing string to int
{
Console.WriteLine(item);
}
}
}
See Also |
