using System; using System.Drawing; using System.Windows.Forms; using System.Collections.Generic; public class Exercise : Form { private ListBox lbxNumbers; public Exercise() { InitializeComponent(); } void InitializeComponent() { lbxNumbers = new ListBox(); lbxNumbers.Width = 100; lbxNumbers.Location = new System.Drawing.Point(12, 12); Text = "Numbers"; MinimizeBox = false; MaximizeBox = false; Controls.Add(this.lbxNumbers); Load += new EventHandler(ExerciseLoad); Size = new System.Drawing.Size(130, 145); StartPosition = FormStartPosition.CenterScreen; } private void ExerciseLoad(object sender, EventArgs e) { Collection<double> coll = new Collection<double>(); coll.Add(1244.1250); coll.Add(224.52); coll.Add(64525.38); coll.Add(1005.36); coll.Add(86.828); coll.Add(2448.362); coll.Add(632.04); for (var n = 0; n < coll.Count; n++ ) lbxNumbers.Items.Add(coll[n]); } [STAThread] public static int Main() { System.Windows.Forms.Application.Run(new Exercise()); return 0; } } public class Collection<T> { private T[] items; private int size; public Collection() { size = 0; items = new T[10]; } public int Count { get { return size; } } public void Add(T item) { items[size] = item; size++; } public T this[int index] { get { return items[index]; } set { items[index] = value; size++; } } } This would produce
One of the most regular ways to check the members of a collection consists of visiting each and taking the appropriate action. You do this by using the foreach loop. This would be done as follows: private void ExerciseLoad(object sender, EventArgs e) { Collection This would produce: Error 1 foreach statement cannot operate on variables of type 'Collection<double>' because 'Collection<double>' does not contain a public definition for 'GetEnumerator' C:\. . .\Exercise.cs 42 9 IntroductionToCollections2 In order to use foreach, there are preliminary actions that a collection class must take. To support this, the .NET Framework provides some mechanism through the IEnumerator and the IEnumerable interfaces.
The IEnumerator<> (generic) interface provides the means of identifying the class that holds a sample of the items that will be enumerated. The IEnumerator<> interface inherits from the IEnumerator (non-generic) and the IDisposable (non-generic) interfaces. To use the functionality of the IEnumerator interface, you must create a class that implements it. You can start the class as follows: using System.Collections.Generic; public class Enumerator<T> : IEnumerator<T> { } If your collection is an array-based list, you can start by declaring the base array in the class: Here is an example: public class Enumerator<T> : IEnumerator<T> { private T[] objects; } If the collection is not array-based, you can declare a variable for the class that would be enumerated. The role of the enumerator is to act on a collection. For this reason, the class should be prepared to receive an external collection. This can be done by passing it to a constructor of the enumerator. Here is an example: public class Enumerator<T> : IEnumerator<T> { private T[] objects; public Enumerator(T[] list) { } } The internal collection would be used in the enumerator class. The external collection would be the source of the values of the list that would be enumerated. For these reasons, you can/should initialize the internal collection with the values of the external list. This can be done as follows: public class Enumerator<T> : IEnumerator<T> { private T[] objects; public Enumerator(T[] list) { this.objects = list; } }
When creating a list, you should have a type of tag, as a field, that allows you to monitor the item that is being currently accessed or used in the list. The IEnumerator interface provides a property that is used to identify the current member of the list. This property is called Current. Because the current item is meant to be viewed only, the Current property is a read-only member. To implement the Current property, you can define its get accessor to return the item at the current position. This can be done as follows: public class Enumerator<T> : IEnumerator<T> { private T[] objects; private int cur; public Enumerator(T[] list) { this.objects = list; } public T Current { get { return objects[cur]; } } } Besides its own version, the IEnumerator<> interface inherits another version of the Current property from its parent and you must implement it too. The Current property of the IEnumerator interface is of type object. You can write its code like that of the previous version. Because both properties use the exact same name, to distinguish them, you should (must) qualify the version of the IEnumerator interface. Here is an example: public class Enumerator<T> : IEnumerator<T> { private T[] objects; private int cur; public Enumerator(T[] list) { this.objects = list; } public T Current { get { return objects[cur]; } } object System.Collections.IEnumerator.Current { get { return objects[cur]; } } }
Although you should be able to identify the current item at any time, when the application starts, before the collection can be enumerated, the tag that is used to monitor the current item should be set to a value before the beginning of the count. This can be done by setting the tag to -1. Here is an example: public class Enumerator<T> : IEnumerator<T> { private T[] objects; private int cur; public Enumerator(T[] list) { this.objects = list; cur = -1; } public T Current { get { return objects[cur]; } } object System.Collections.IEnumerator.Current { get { return objects[cur]; } } } While the collection is being used, at one moment you may want to reset the tag of the current item to its original position. To support this operation, the IEnumerator interface is equipped with a method named Reset that the IEnumerator<> interface inherits. Its syntax is: void Reset(); When implementing this method, simply assign a non-existing value, which is usually -1, to the monitoring tag of the current item. This can be done as follows: public class Enumerator<T> : IEnumerator<T> { private T[] objects; private int cur; public Enumerator(T[] list) { this.objects = list; cur = -1; } public T Current { get { return objects[cur]; } } object System.Collections.IEnumerator.Current { get { return objects[cur]; } } public void Reset() { cur = -1; } } When using the implementer of the IEnumerator<> interface, if you try accessing an item beyond the maximum number of items, the compiler would throw an IndexOutOfRangeException exception.
We know that, when using the items of a collection, one way you could locate one item from another is to be able to jump from one item to the next. This operation is also very important when enumerating a collection. To support this operation, the IEnumerator<> interface inherits the MoveNext() method from its parent. The syntax of the IEnumerator<> interface is: bool MoveNext(); When implementing this method, first increment the tag that monitors the current item of the collection. After incrementing the tag, check whether it is lower than the total number of items. If it is, return true. Otherwise, return false. This can be done as follows: public class Enumerator<T> : IEnumerator<T> { private T[] objects; private int cur; public Enumerator(T[] list) { this.objects = list; cur = -1; } public T Current { get { return objects[cur]; } } object System.Collections.IEnumerator.Current { get { return objects[cur]; } } public void Reset() { cur = -1; } public bool MoveNext() { cur++; if (cur < objects.Length) return true; else return false; } }
In some cases, the object of a generic class consumes resources. These resources should be freed when the object is not used anymore. To assist you with cleaning memory, the IEnumerator<> interface implements the IDisposable interface. This interface has only one method, named Dispose. If you are creating a generic class that is not concerned with resources and would let the compiler take care of that, you can simply provide a body for this method. On the other hand, if you anticipate that the resources used by your class will need to be deallocated, make sure you implement this method properly.
The IEnumerator<> interface is used to set up a collection for enumeration. The IEnumerator<> class doesn't provide the functionality necessary to use foreach. The next step is to implement another interface called IEnumerable. While the IEnumerator<> (generic) interface is used to identify the class that holds each value that will be visited, the IEnumerable<> (generic) interface inherits from the IEnumerator (non-generic) interface that is used to communicate with the collection whose items will be enumerated. For this reason, when implementing the IEnumerable<> interface, you should provide the means of accessing the external collection. This can be done by passing a collection of the class that holds the values, to a constructor of the IEnumerable<> implementer.
To implement the IEnumerable<> interface, start by deriving a class from it. While the class implemented by the IEnumerator interface represents an object, the class that implements the interface is a collection. Here is an example: using System.Collections.Generic;
public class Collection<T> : IEnumerable<T>
{
. . . No Change
}
The new class does not know what collection it will be asked to enumerate. For this reason, in the new class, you should declare a member variable of the class that holds the values that will be enumerated. If the collection is array-based, we saw that you can create the field as follows: public class Collection<T> : IEnumerable<T> { private T[] items; private int size; public Collection() { size = 0; items = new T[10]; } public int Count { get { return size; } } public void Add(T item) { items[size] = item; size++; } public T this[int index] { get { return items[index]; } set { items[index] = value; size++; } } }
To support the use of the foreach loop, the IEnumerable<> interface is equipped with a method named GetEnumerator that you must implement. The IEnumerable<>.GetEnumerator() method returns an IEnumerator<> object. Its syntax is: IEnumerator<T> GetEnumerator(); When implementing this method, you can return an object of the class that implements the IEnumerator<> interface, passing it the collection that was declared in the IEnumerable<> implementer. This can be done as follows: public class Collection<T> : IEnumerable<T> { private T[] items; . . . No Change public IEnumerator<T> GetEnumerator() { return new Enumerator<T>(items); } } Besides this version, the IEnumerable<> interface inherits another version of the GetEnumerator() method from IEnumerable. Its syntax is: IEnumerator GetEnumerator(); The IEnumerator.GetEnumerator() method returns an IEnumerator (non-generic) value. Since these two methods have the same name, to distinguish them, you must qualify the version of the IEnumerator interface. Here is an example: using System.Collections.Generic;
public class Collection<T> : IEnumerable<T>
{
private T[] items;
. . . No Change
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return new Enumerator<T>(items);
}
}
After implementing the IEnumerator<> and the IEnumerable<> interfaces, you can then use the foreach loop. To start, you must prepare the collection and its items for processing. To enumerate the collection, declare a variable based on the implementer of the IEnumerable<> and pass the collection to its constructor. Once this is done, you can then use the foreach. Here is an example: using System; using System.Drawing; using System.Windows.Forms; using System.Collections.Generic; public class Exercise : Form { private ListBox lbxNumbers; public Exercise() { InitializeComponent(); } void InitializeComponent() { lbxNumbers = new ListBox(); lbxNumbers.Size = new Size(80, 140); lbxNumbers.Location = new System.Drawing.Point(12, 12); Text = "Numbers"; MinimizeBox = false; MaximizeBox = false; Controls.Add(this.lbxNumbers); Load += new EventHandler(ExerciseLoad); Size = new System.Drawing.Size(105, 185); StartPosition = FormStartPosition.CenterScreen; } private void ExerciseLoad(object sender, EventArgs e) { Collection<double> coll = new Collection<double>(); coll.Add(1244.1250); coll.Add(224.52); coll.Add(64525.38); coll.Add(1005.36); coll.Add(86.828); coll.Add(2448.362); coll.Add(632.04); coll.Add(102.1620); coll.Add(558.6062); coll.Add(630302.2068); foreach (var n in coll) lbxNumbers.Items.Add(n); } [STAThread] public static int Main() { System.Windows.Forms.Application.Run(new Exercise()); return 0; } } public class Enumerator<T> : IEnumerator<T> { private T[] objects; private int cur; public Enumerator(T[] list) { this.objects = list; cur = -1; } public T Current { get { return objects[cur]; } } object System.Collections.IEnumerator.Current { get { return objects[cur]; } } public void Reset() { cur = -1; } public bool MoveNext() { cur++; if (cur < objects.Length) return true; else return false; } public void Dispose() { return; } } public class Collection<T> : IEnumerable<T> { private T[] items; private int size; public Collection() { size = 0; items = new T[10]; } public int Count { get { return size; } } public void Add(T item) { items[size] = item; size++; } public T this[int index] { get { return items[index]; } set { items[index] = value; size++; } } public IEnumerator<T> GetEnumerator() { return new Enumerator<T>(items); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return new Enumerator<T>(items); } } This would produce:
The IEnumerable<> interface makes it possible to enumerate the members of a collection that implements this interface. In the previous sections, we went through some details of this exercise. Because this can be overwhelming sometimes, the C# language provides the yield keyword you can use. There are various ways you can use yield. One way you can use yield consists of creating a method that returns an IEnumerator<> value. When implementing the method, use a while loop to get to each member of the collection. In the loop, use a yield return expression to return the value that is currently visited. Also make sure you can navigate from the current to the next item, and stop when you get to the last. Here is an example: using System; using System.Drawing; using System.Windows.Forms; using System.Collections.Generic; public class Exercise : Form { private ListBox lbxNumbers; public Exercise() { InitializeComponent(); } void InitializeComponent() { lbxNumbers = new ListBox(); lbxNumbers.Size = new Size(80, 140); lbxNumbers.Location = new System.Drawing.Point(12, 12); Text = "Numbers"; MinimizeBox = false; MaximizeBox = false; Controls.Add(this.lbxNumbers); Load += new EventHandler(ExerciseLoad); Size = new System.Drawing.Size(105, 185); StartPosition = FormStartPosition.CenterScreen; } private void ExerciseLoad(object sender, EventArgs e) { Collection Another technique to use yield consists of creating a method that returns an IEnumerable<> object. Inside the method, you can use a while loop to access each member of the collection and return it. Once again, you combine yield and return to indicate that the method is an iterator that returns a member of the collection. At the end of each loop, find a way to get to the next member unless you have gotten to the end of the collection. As done previously, you can use a local variable to take care of this. After creating the method, when using the collection, make sure you call the method. Here is an example: using System; using System.Drawing; using System.Windows.Forms; using System.Collections.Generic; public class Exercise : Form { private ListBox lbxNumbers; public Exercise() { InitializeComponent(); } void InitializeComponent() { lbxNumbers = new ListBox(); lbxNumbers.Size = new Size(95, 140); lbxNumbers.Location = new System.Drawing.Point(12, 12); Text = "Names"; MinimizeBox = false; MaximizeBox = false; Controls.Add(this.lbxNumbers); Load += new EventHandler(ExerciseLoad); Size = new System.Drawing.Size(125, 185); StartPosition = FormStartPosition.CenterScreen; } private void ExerciseLoad(object sender, EventArgs e) { Collection<string> names = new Collection<string>(); names.Add("Alain Soundjock"); names.Add("Paul motto"); names.Add("Jerry Fields"); names.Add("Ernest Engolo"); names.Add("Charles Benton"); names.Add("Henry Mobies"); names.Add("Thomas Swanson"); names.Add("Robert Hanson"); names.Add("Hela Balla"); names.Add("Daniel Feldman"); foreach (var name in names.AllItems()) lbxNumbers.Items.Add(name); } [STAThread] public static int Main() { System.Windows.Forms.Application.Run(new Exercise()); return 0; } } public class Collection<T> { private T[] items; private int size; public Collection() { size = 0; items = new T[10]; } public int Count { get { return size; } } public void Add(T number) { items[size] = number; size++; } public T this[int index] { get { return items[index]; } set { items[index] = value; size++; } } public IEnumerable<T> AllItems() { int counter = 0; while (counter < Count) { yield return items[counter]; counter++; } } } This would produce:
You can create the method as static. In this case you can pass the collection class as argument. When calling the method, use the name of the collection class to call the method and pass the variable as argument. Here is an example where the method is created in the body of the collection class: using System; using System.Drawing; using System.Windows.Forms; using System.Collections.Generic; public class Exercise : Form { private ListBox lbxNumbers; public Exercise() { InitializeComponent(); } void InitializeComponent() { lbxNumbers = new ListBox(); lbxNumbers.Size = new Size(95, 140); lbxNumbers.Location = new System.Drawing.Point(12, 12); Text = "Names"; MinimizeBox = false; MaximizeBox = false; Controls.Add(this.lbxNumbers); Load += new EventHandler(ExerciseLoad); Size = new System.Drawing.Size(125, 185); StartPosition = FormStartPosition.CenterScreen; } private void ExerciseLoad(object sender, EventArgs e) { Collection<string > names = new Collection<string >(); names.Add("Alain Soundjock"); names.Add("Paul motto"); names.Add("Jerry Fields"); names.Add("Ernest Engolo"); names.Add("Charles Benton"); names.Add("Henry Mobies"); names.Add("Thomas Swanson"); names.Add("Robert Hanson"); names.Add("Hela Balla"); names.Add("Daniel Feldman"); foreach (var name in Collection<string>.AllItems(names)) lbxNumbers.Items.Add(name); } [STAThread] public static int Main() { System.Windows.Forms.Application.Run(new Exercise()); return 0; } } public class Collection<T> { private T[] items; private int size; public Collection() { size = 0; items = new T[10]; } public int Count { get { return size; } } public void Add(T number) { items[size] = number; size++; } public T this[int index] { get { return items[index]; } set { items[index] = value; size++; } } public static IEnumerable<T> AllItems(Collection<T> lst) { int counter = 0; while (counter < lst.Count) { yield return lst.items[counter]; counter++; } } }
|
|||||||||||||||||||||||||||||||||||||||||||||||