R
raylopez99
Refer to the below example.
It's all very fine and well, and I wholeheartedly agree with it, but
it seems foreach is pretty useless for anything other than a one-
member variable class (in the below, it's the class member "private
string[] elements;")
If you want to have a composite class, how do you implement the
required MoveNext, Current and Reset method of IEnumerator? For
example, if the below Tokens class has a member: private int []
intelements; or even more complicated class private myClass[]
myClassArray; etc.
Seems to me the more logical thing (and which I do) is to rely on
standard collection classes instead of a 'custom' foreach for member
variables, and, when necessary, the trick of moving the iteration
logic into a method returning a generic IEnumerable<T> and letting the
compiler take care of the rest, along the lines of:
int[] ints = {1,2,3,4,5}; //in your class
public static IEnumerable <int> GetEvenInts() {foreach (int i in ints)
if(i%2 == 0) yield return i;}} //static method also in your class.
That's it. No need to create a complicated IEnumerable that inherits
and/or references an IEnumerator as the below.
Any thoughts? Or am I right as usual?
RL
http://msdn.microsoft.com/en-us/library/aa288462(VS.71).aspx
C# Programmer's Reference
Collection Classes Tutorial
This tutorial shows how to implement a collection class that can be
used with the foreach statement.
Sample Files
See Collection Classes Sample to download and build the sample files
discussed in this tutorial.
Further Reading
* foreach, in
* 8.8.4 The foreach statement
* Command Line Parameters Tutorial
* Arrays Tutorial
Tutorial
The 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 1
The 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.
Copy Code
// 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);
}
}
}
Output
Copy Code
This
is
a
well
done
program.
Code Discussion
In the preceding example, the following code is used to Tokens by
breaking "This is a well-done program." into tokens (using ' ' and '-'
as separators) and enumerating those tokens with the foreach
statement:
Copy Code
Tokens f = new Tokens("This is a well-done program.",
new char[] {' ','-'});
foreach (string item in f)
{
Console.WriteLine(item);
}
Notice that, internally, Tokens uses an array, which implements
IEnumerator and IEnumerable itself. The code sample could have
leveraged the array's enumeration methods as its own, but that would
have defeated the purpose of this example.
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 GetEnumerator,
MoveNext, Reset, and Current members, it will work with foreach.
Omitting the interfaces has the advantage of allowing you to define
the return type of Current to be more specific than object, thereby
providing type-safety.
For example, starting with the sample code above, change the following
lines:
Copy Code
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 Current returns a string, the compiler can detect when an
incompatible type is used in a foreach statement:
Copy Code
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 2
This sample is equivalent in function to Example 1, but it provides
additional type-safety in C# while maintaining interoperability with
other languages.
Copy Code
// 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);
}
}
}
It's all very fine and well, and I wholeheartedly agree with it, but
it seems foreach is pretty useless for anything other than a one-
member variable class (in the below, it's the class member "private
string[] elements;")
If you want to have a composite class, how do you implement the
required MoveNext, Current and Reset method of IEnumerator? For
example, if the below Tokens class has a member: private int []
intelements; or even more complicated class private myClass[]
myClassArray; etc.
Seems to me the more logical thing (and which I do) is to rely on
standard collection classes instead of a 'custom' foreach for member
variables, and, when necessary, the trick of moving the iteration
logic into a method returning a generic IEnumerable<T> and letting the
compiler take care of the rest, along the lines of:
int[] ints = {1,2,3,4,5}; //in your class
public static IEnumerable <int> GetEvenInts() {foreach (int i in ints)
if(i%2 == 0) yield return i;}} //static method also in your class.
That's it. No need to create a complicated IEnumerable that inherits
and/or references an IEnumerator as the below.
Any thoughts? Or am I right as usual?
RL
http://msdn.microsoft.com/en-us/library/aa288462(VS.71).aspx
C# Programmer's Reference
Collection Classes Tutorial
This tutorial shows how to implement a collection class that can be
used with the foreach statement.
Sample Files
See Collection Classes Sample to download and build the sample files
discussed in this tutorial.
Further Reading
* foreach, in
* 8.8.4 The foreach statement
* Command Line Parameters Tutorial
* Arrays Tutorial
Tutorial
The 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 1
The 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.
Copy Code
// 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);
}
}
}
Output
Copy Code
This
is
a
well
done
program.
Code Discussion
In the preceding example, the following code is used to Tokens by
breaking "This is a well-done program." into tokens (using ' ' and '-'
as separators) and enumerating those tokens with the foreach
statement:
Copy Code
Tokens f = new Tokens("This is a well-done program.",
new char[] {' ','-'});
foreach (string item in f)
{
Console.WriteLine(item);
}
Notice that, internally, Tokens uses an array, which implements
IEnumerator and IEnumerable itself. The code sample could have
leveraged the array's enumeration methods as its own, but that would
have defeated the purpose of this example.
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 GetEnumerator,
MoveNext, Reset, and Current members, it will work with foreach.
Omitting the interfaces has the advantage of allowing you to define
the return type of Current to be more specific than object, thereby
providing type-safety.
For example, starting with the sample code above, change the following
lines:
Copy Code
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 Current returns a string, the compiler can detect when an
incompatible type is used in a foreach statement:
Copy Code
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 2
This sample is equivalent in function to Example 1, but it provides
additional type-safety in C# while maintaining interoperability with
other languages.
Copy Code
// 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);
}
}
}