13

 

Collection Objects

 

Earlier we used an array in a foreach. If the array had three members, the foreach got executed three times and each time the variable i had a different value. The concept described below is called a collection class. It is a class that returns a value each time till the values run out, thus making it easier for us to iterate through the array.

 

a.cs

public class zzz

{

public static void Main()

{

yyy f = new yyy();

foreach (string i in f)

{

}

}

}

class yyy

{

}

 

 

Compiler Error      

a.cs(6,1): error CS1579: foreach statement cannot operate on variables of type ‘yyy’ because ‘yyy’ does not contain a definition for ‘GetEnumerator’, or it is inaccessible

 

To use yyy in a foreach as a collection class, foreach requires a function GetEnumerator.

 

a.cs

public class zzz

{

public static void Main()

{

yyy f = new yyy();

foreach (string i in f)

{

}

}

}

class yyy

{

public int GetEnumerator()

{

}

}

 

Compiler Error

a.cs(6,1): error CS1579: foreach statement cannot operate on variables of type 'yyy' because 'int' does not contain a definition for 'MoveNext', or it is inaccessible

a.cs(13,12): error CS0161: 'yyy.GetEnumerator()': not all code paths return a value

 

Foreach obviously tries to execute the function called GetEnumerator. This function should not return an int but something else as the error suggests.

 

 

 

 

a.cs

using System.Collections;

public class zzz

{

public static void Main()

{

yyy f = new yyy();

foreach (string i in f)

{

}

}

}

class yyy

{

public IEnumerator GetEnumerator()

{

return new xxx();

}

}

class xxx : IEnumerator

{

}

 

Compiler Error

a.cs(19,7): error CS0535: ‘xxx’ does not implement interface member ‘System.Collections.IEnumerator.MoveNext()’

a.cs(19,7): error CS0535: ‘xxx’ does not implement interface member ‘System.Collections.IEnumerator.Reset()’

a.cs(19,7): error CS0535: ‘xxx’ does not implement interface member ‘System.Collections.IEnumerator.Current’

 

IEnumerator is an interface which has three functions MoveNext, Reset and Current and xxx has to implement all of them to remove the compiler errors.

 

a.cs

using System.Collections;

public class zzz

{

public static void Main()

{

yyy f = new yyy();

foreach (string i in f)

{

System.Console.WriteLine(i);

}

}

}

class yyy

{

public IEnumerator GetEnumerator()

{

return new xxx();

}

}

class xxx : IEnumerator

{

public bool MoveNext()

{

System.Console.WriteLine(“MoveNext”);

return true;

}

public void Reset()

{

System.Console.WriteLine(“Reset”);

}

public object Current

{

get

{

System.Console.WriteLine(“Current”);

return “hi”;

}

}

}

 

Run the program and you will notice that the output does not stop. It goes on forever.

IEnumerator is an interface which belongs to the namespace System.Collections. foreach first calls the function GetEnumerator from yyy. It expects this function to return an object like IEnumerator. It then calls the function MoveNext from this returned object. If MoveNext returns true it knows that there is some data to be read and it calls the property Current to access this data. From Current the get accessor gets called which always returns “hi” in our case. Then MoveNext gets called and if it returns false, we quit out of the foreach statement. As MoveNext always returns true, we go into an indefinite loop.

 

a.cs

using System.Collections;

public class zzz

{

public static void Main()

{

yyy f = new yyy();

foreach (string i in f)

{

System.Console.WriteLine(i);

}

}

}

class yyy

{

public IEnumerator GetEnumerator()

{

return new xxx();

}

}

class xxx : IEnumerator

{

public string []  a = new string[3] {“hi” , “bye” ,”no”};

public int i = -1;

public bool MoveNext()

{

i++;

System.Console.WriteLine(“MoveNext” + i);

if ( i == 3)

return false;

else

return true;

}

public void Reset()

{

System.Console.WriteLine(“Reset”);

}

public object Current

{

get

{

System.Console.WriteLine(“Current “ + a[i]);

return a[i];

}

}

}

 

Output

MoveNext0

Current hi

hi

MoveNext1

Current bye

bye

MoveNext2

Current no

no

MoveNext3

 

We have created an array a which has 3 members and initialized them respectively to hi, bye and no by giving the strings in {} immediately after the new. Each time MoveNext gets called the variable i is increased by 1. If the value of i is 3, we have no more strings to return and thus we return false, else we return true. The variable i keeps track of how many times the function MoveNext is being called. As MoveNext returns true, Current gets called which returns a string from the array using i as the offset. Thus we can iterate through the entire array depending upon the length.

 

a.cs

using System.Collections;

public class zzz

{

public static void Main()

{

yyy f = new yyy(“This is Great”);

foreach (string i in f)

{

System.Console.WriteLine(i);

}

}

}

class yyy

{

string t;

public yyy(string t1)

{

t = t1;

}

public IEnumerator GetEnumerator()

{

return new xxx(t);

}

}

class xxx : IEnumerator

{

public string [] a;

public xxx(string t3)

{

char [] b = new char[1];

b[0] = ‘ ‘;

a = t3.Split(b);

}

public int i = -1;

public bool MoveNext()

{

i++;

System.Console.WriteLine(“MoveNext “ + i);

if ( i == a.Length)

return false;

else

return true;

}

public void Reset()

{

System.Console.WriteLine(“Reset”);

}

public object Current

{

get

{

System.Console.WriteLine(“Current “ + a[i]);

return a[i];

}

}

}

 

Output

MoveNext 0

Current This

This

MoveNext 1

Current is

is

MoveNext 2

Current Great

Great

MoveNext 3

 

Pretty big program. At the time of creating a yyy object we are passing a string to the constructor. Thus the yyy constructor gets called first. The constructor stores this string in variable t. The foreach statement calls GetEnumerator which now creates a xxx object passing it the string through t. The constructor of xxx now gets called. Every string class has a member function called Split. Split will break up a string on certain characters which we call delimiters. In this case, we want our string to be broken up whenever we encounter a space. The Split function requires an array of chars which it can use as a delimiter. The reason it requires an array is because we may have more than one char that we would like to break the string on. Like earlier, the array a now contains the array of strings. The last change is the condition in the if statement. Earlier we used a constant number, now we use a member, Length, of an array which stores the length of the array or the number of members. Thus the class yyy can now be used as a collection class which enumerates the individual words in the string. The function Reset for some reason never ever gets called.