foreach pretty useless for composite classes, don't ya thunk?


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);
}
}
}
 
Ad

Advertisements

M

Marc Gravell

You are looking at the 1.1 documentation. There is a *reason* that
iterator blocks were introduced in C# 2.0.

"as usual"? I nearly choked on my coffee there...

Marc
 
F

Family Tree Mike

An example of a slightly more complicated class might be to have a collection
of first names, and a collection of last names in your class. The
IEnumerable functions then could return the complete set of all possible
combinations of first and last name.
 
G

Göran Andersson

raylopez99 said:
Any thoughts? Or am I right as usual?

I haven't read your usually length "article" so I don't have any
specific thoughts about it, but I expect that you are as right as you
usually are, i.e. not at all.
 
B

Bill Butler

Any thoughts? Or am I right as usual?

I don't think "right" is the correct word.
There are many other words that could fill in the blank though.

"Or am I _______ as usual?"
 
B

Brian Gideon

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);
      }
   }



}

Can you summarize your point in a single paragraph and a single
example?
 
Ad

Advertisements

R

raylopez99

An example of a slightly more complicated class might be to have a collection
of first names, and a collection of last names in your class.  The
IEnumerable functions then could return the complete set of all possible
combinations of first and last name.

What would be that example, FTM? The example I gave was from MDN and
it in turn was from an earlier example (same author, he just didn't
have the latter part "You can have the best of both worlds.."


RL
 
R

raylopez99

I don't think "right" is the correct word.
There are many other words that could fill in the blank though.

    "Or am I _______ as usual?"

Ad homenium attack noted, and you don't have an answer to my question,
which is why do we need foreach.

RL
 
R

raylopez99

I haven't read your usually length "article" so I don't have any
specific thoughts about it, but I expect that you are as right as you
usually are, i.e. not at all.

Then, if you've not read the article, you are essentially flame
baiting. Noted. Goran is a flame baiter.

RL
 
R

raylopez99

You are looking at the 1.1 documentation. There is a *reason* that
iterator blocks were introduced in C# 2.0.

No, this example was from the MSDN, which actually incorporates C#
3.0.

Any other guesses?
"as usual"? I nearly choked on my coffee there...

Truth hurts, eh coffee drinking Marc?

RL
 
B

Brian Gideon

Ad homenium attack noted, and you don't have an answer to my question,
which is why do we need foreach.

It provides a convenient way of using IEnumerable and IEnumerator
classes.

At first I thought you were asking why someone would bother
implementing their own IEnumerable and IEnumerator classes. There was
also some talk of more than one member variable in classes, etc. Like
I said earlier...can you summarize your point in one concise paragraph
and example?
 
Ad

Advertisements

F

Family Tree Mike

The url you included contains "...(vs.71)..", which means it was written for
VS 2003, and therefore .Net 1.1.
 
F

Family Tree Mike

Sorry, I cannot think back how to do this in VS 2003.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NameGenerator
{
public class Namer : IEnumerable<string>
{
public string [ ] FirstNames;
public string [ ] LastNames;

IEnumerator<string> IEnumerable<string>.GetEnumerator ()
{
for (int idxLast = 0; idxLast < LastNames.Count (); ++idxLast)
for (int idxFirst = 0; idxFirst < FirstNames.Count (); ++idxFirst)
yield return string.Format ( "{0} {1}",
FirstNames [idxFirst], LastNames [idxLast] );
}

System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator ()
{
for (int idxLast = 0; idxLast < LastNames.Count (); ++idxLast)
for (int idxFirst = 0; idxFirst < FirstNames.Count (); ++idxFirst)
yield return string.Format ( "{0} {1}",
FirstNames [idxFirst], LastNames [idxLast] );
}
}


class Program
{
static void Main ( string [ ] args )
{
Namer nlist = new Namer ();
nlist.FirstNames = new string [ ] { "Ray", "Casey", "Zanaida" };
nlist.LastNames = new string [ ] { "Anthony", "Gonzales", "Lopez",
"Jones" };

foreach (string s in nlist)
Console.Out.WriteLine ( string.Format ( "The next name is: {0}", s )
);

Console.In.ReadLine ();
}
}
}
 
R

raylopez99

It provides a convenient way of using IEnumerable and IEnumerator
classes.

At first I thought you were asking why someone would bother
implementing their own IEnumerable and IEnumerator classes.  

I was.
There was
also some talk of more than one member variable in classes, etc.  

Yes, that was another point.
Like
I said earlier...can you summarize your point in one concise paragraph
and example?

That would be the original post--the observation that IEnmerable (the
interface) and IEnumerator (with implements three methods, namely bool
MoveNext(), object Current {get;} and void Reset();)--is pretty
worthless for anything other than a one-member class.

The program was simply to demonstrate one such useless
implementation. I further observed (and this is from a textbook that
made the same point) that you can simply declare a static IEnumerable
<T> and let the compiler take care of any implementation, rather than
go the IEnumerable/IEnumerator route.

RL
 
R

raylopez99

On Sep 19, 10:51 am, Family Tree Mike
An example of a slightly more complicated class might be to have a
collection
of first names, and a collection of last names in your class. The
IEnumerable functions then could return the complete set of all
possible
combinations of first and last name

////////////

Thanks FTM. I guess your program was to demonstrate the above. I
appreciate it, but I note that essentially you can do the same thing
with a simple call to a function that does a sort and so forth, at the
cost of foregoing the 'foreach' notation.

In any event, I'm not sure your code would work however, since I don't
see any of the three required functions of IEnumerator--MoveNext,
Resent, and Current.

RL
 
Ad

Advertisements

B

Brian Gideon

I was.


Yes, that was another point.


That would be the original post--the observation that IEnmerable (the
interface) and IEnumerator (with implements three methods, namely bool
MoveNext(), object Current {get;} and void Reset();)--is pretty
worthless for anything other than a one-member class.

The program was simply to demonstrate one such useless
implementation.  I further observed (and this is from a textbook that
made the same point) that you can simply declare a static IEnumerable
<T> and let the compiler take care of any implementation, rather than
go the IEnumerable/IEnumerator route.

RL

Regarding the first point...you don't necessarily have to do the work
of implementing an IEnumerator now. C# 2.0 added the ability to use
the 'yields' keyword that does this for you. But, that ability hasn't
always existed. So my answer to that question is that most of the
time you probably wouldn't bother doing it manually.

Regarding being able to create an IEnumerable and IEnumerator that
incorporates iterations over more than one member variable...Were you
talking about the case where you want one iteration to cover multiple
class members or different iterations over different class members.
More precisely, are you 1) wanting one iterator to enumerator some
combination of items from more than one variable or 2) be able to
define different iterators that enumerator items from different
variables. It's possible to accomplish either one. In the first case
it is only slightly more complicated than a trivial case. You just
have to come up with the appropriate logic for the MoveNext method (or
the equivalent if using the yields keyword). I've done it before. In
the second case you just add more properties to the class that return
the appropriate IEnumerable classes. Take a look at the Dictionary
class. It has Values and Keys properties that do just that.

And to sum it all up the foreach construct provides an elegant way for
working with IEnumerable and IEnumerator classes. Sure, you can call
the Reset, MoveNext, and Current members yourself in a while loop.
But, why do that when the compiler will do it for you. Plus, foreach
will automatically call Dispose if your IEnumerator just happens to
implement IDisposable as well.
 
B

Brian Gideon

An example of a slightly more complicated class might be to have a
collection
of first names, and a collection of last names in your class.  The
IEnumerable functions then could return the complete set of all
possible
combinations of first and last name

////////////

Thanks FTM.  I guess your program was to demonstrate the above.  I
appreciate it, but I note that essentially you can do the same thing
with a simple call to a function that does a sort and so forth, at the
cost of foregoing the 'foreach' notation.

Just to make sure I understand what you're saying...you could create a
function that generates a new collection whose elements are the result
of the operation that figures out all possible combinations of the
first and last name right?

The disadvantage of that is that the entire operation has to complete
before the caller can work with that new collection. If you choose to
go down the IEnumerable/IEnumerator route then the caller could exit
out of the foreach enumeration early and *before* the operation (to
figure out the combinations of first and last name) has completed.
In any event, I'm not sure your code would work however, since I don't
see any of the three required functions of IEnumerator--MoveNext,
Resent, and Current.

You don't see them because 1) the yield keyword was used to instruct
the compiler to create the IEnumerator automatically and 2) the
foreach keyword was used to instruct the compiler to call those
methods on IEnumerator automatically. Use ILDASM to examine to the
resultant assembly.
 
R

raylopez99

Regarding being able to create an IEnumerable and IEnumerator that
incorporates iterations over more than one member variable...Were you
talking about the case where you want one iteration to cover multiple
class members or different iterations over different class members.
More precisely, are you 1) wanting one iterator to enumerator some
combination of items from more than one variable or 2) be able to
define different iterators that enumerator items from different
variables.  It's possible to accomplish either one.  In the first case
it is only slightly more complicated than a trivial case.  You just
have to come up with the appropriate logic for the MoveNext method (or
the equivalent if using the yields keyword).  I've done it before.  In
the second case you just add more properties to the class that return
the appropriate IEnumerable classes.  Take a look at the Dictionary
class.  It has Values and Keys properties that do just that.

Thanks Brian--if you have an example "I've done it before" that you
can post without giving away a client's source code or whatever,
please feel free. As for the Dictionary Class, I would not even know
where to find the source code, if it exists in my library somewhere.

You other point about 'yields' hiding IEnumerator is interesting and I
will look into FTM's example again with this in mind.

Cheers,

RL
 
Ad

Advertisements

B

Brian Gideon

Thanks Brian--if you have an example "I've done it before" that you
can post without giving away a client's source code or whatever,
please feel free.  As for the Dictionary Class, I would not even know
where to find the source code, if it exists in my library somewhere.

No need. FTM already posted an example that generates an IEnumerator
that enumerates every combination of first and last names based on two
string arrays (one the holds just first names, and the other holds
just last names). FTM just happen to use the yield keyword method.
It could have been done by manually declaring and implementing the
IEnumerator but that would be more work.

As for the Dictionary I was focused more on the public interface and
not necessary on how Microsoft made that happen. I just wanted to
present an example design pattern for case #2. Fortunately, Microsoft
has made the source code for v3.5 of the framework available. I'm not
sure about the details for downloading it as I have yet to do that,
but it is available nevertheless. Of course, you could always use
Reflector or even ILDASM to examine the framework assemblies. The
Dictionary class is either in mscorlib.dll or System.dll.
You other point about 'yields' hiding IEnumerator is interesting and I
will look into FTM's example again with this in mind.

Typo...I meant the 'yield' keyword. You used it in the example of the
GetEventInts in your original post. Again use Reflector or ILDASM to
see what the compiler did for you.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top