Learning C#: Custom Collection Classes in C#

Codeteddy article series

Introduction

Being a .NET developer, we all are familiar with collections and generics. We know ArrayLists, Arrays, List and Dictionary etc. as collection classes through which we can iterate. This article will explain on how one can create their own custom collection class, which can be iterated through. The article follows step by step process to create a custom collection class to know what actually it takes to create a collection class. The purpose of the article is to learn the concept of collection classes through step by step practical implementations.


Prerequisites

The prerequisite to learn how to create a custom class is not more than to know how to code in C#. This article uses a console Application named CollectionClassConcept, which contains an empty class named CollectionClass. We’ll use this initial setup to learn about collection classes.

C#

Step 1

Considering this, we already have a collection class. It does not matter, if it is empty for now. Try to access the CollectionClass in Main method of Program.cs, create an instance of the class and iterate through it.

CollectionClass

  1. namespace CollectionClassConcept
  2. {
  3.   public class CollectionClass
  4.   {
  5.   }
  6. }

Program.cs

  1. using System;
  2. namespace CollectionClassConcept
  3. {
  4.   class Program
  5.   {
  6.     static void Main(string[] args)
  7.     {
  8.       CollectionClass collectionClass=new CollectionClass();
  9.       foreach (string  collection in collectionClass)
  10.       {
  11.         Console.WriteLine(collection);
  12.       }
  13.     }
  14.   }
  15. }

When the solution is compiled, it ends up having a compile time error, as shown below.

C#
Error

foreach statement cannot operate on variables of type ‘CollectionClassConcept.CollectionClass’ because ‘CollectionClassConcept.CollectionClass’ does not contain a public definition for ‘GetEnumerator’

Step 2

As the error suggests that a class needs to have GetEnumerator method for it to be iterative. Thus, add a new GetEnumerator() method into the CollectionClass class.

CollectionClass

  1. namespace CollectionClassConcept
  2. {
  3.   public class CollectionClass
  4.   {
  5.     public int GetEnumerator()
  6.     {
  7.     }
  8.   }
  9. }

Program.cs

  1. using System;
  2. namespace CollectionClassConcept
  3. {
  4.   class Program
  5.   {
  6.     static void Main(string[] args)
  7.     {
  8.       CollectionClass collectionClass=new CollectionClass();
  9.       foreach (string  collection in collectionClass)
  10.       {
  11.         Console.WriteLine(collection);
  12.       }
  13.     }
  14.   }
  15. }

When the solution is compiled, it ends up having a compile time error, as shown below.

C#

Error

  • CS0161 ‘CollectionClass.GetEnumerator()’: not all code paths return a value
  • CS0117 ‘int’ does not contain a definition for ‘Current’
  • CS0202 foreach requires that the return type ‘int’ of ‘CollectionClass.GetEnumerator()’ must have a suitable public MoveNext method and public Current property

Now, in the code given above, foreach here makes an attempt to execute the GetEnumertaor method, but fails because it expects a MoveNext method and a property named Current. In addition to it, also expect the method to return any value other than integer.

Step 3

At this point, one can create a new class named CollectionEnumerator implementing the interface named IEnumerator and return the instance of this class from GetEnumerator method of CollectionClass.

CollectionEnumerator

  1. using System.Collections;
  2. namespace CollectionClassConcept
  3. {
  4.   public class CollectionEnumerator : IEnumerator
  5.   {
  6.   }
  7. }

CollectionClass

  1. using System.Collections;
  2. namespace CollectionClassConcept
  3. {
  4.   public class CollectionClass
  5.   {
  6.     public IEnumerator GetEnumerator()
  7.     {
  8.       return new CollectionEnumerator();
  9.     }
  10.   }
  11. }

Program.cs

  1. using System;
  2. namespace CollectionClassConcept
  3. {
  4.   class Program
  5.   {
  6.     static void Main(string[] args)
  7.     {
  8.       CollectionClass collectionClass=new CollectionClass();
  9.       foreach (string  collection in collectionClass)
  10.       {
  11.         Console.WriteLine(collection);
  12.       }
  13.     }
  14.   }
  15. }

C#

Error

  • ‘CollectionEnumerator’ does not implement an interface member ‘IEnumerator.Current’.
  • ‘CollectionEnumerator’ does not implement an interface member ‘IEnumerator.MoveNext()’.
  • ‘CollectionEnumerator’ does not implement an interface member ‘IEnumerator.Reset()’.

Thus, it is very clear that the IEnumerator interface has the class CollectionEnumerator, which has three methods and are required to be implemented in the child class.

Step 4

Let’s try to implement these methods in the CollectionEnumerator class and see the results.

CollectionEnumerator

  1. using System.Collections;
  2. namespace CollectionClassConcept
  3. {
  4.   public class CollectionEnumerator : IEnumerator
  5.   {
  6.     public bool MoveNext()
  7.     {
  8.       System.Console.WriteLine(“Inside MoveNext Method”);
  9.       return true;
  10.     }
  11.     public void Reset()
  12.     {
  13.       System.Console.WriteLine(“Inside Reset Method”);
  14.     }
  15.     public object Current
  16.     {
  17.       get
  18.       {
  19.         System.Console.WriteLine(“Inside Current Property”);
  20.         return “Current Property”;
  21.       }
  22.     }
  23.   }
  24. }

CollectionClass

  1. using System.Collections;
  2. namespace CollectionClassConcept
  3. {
  4.   public class CollectionClass
  5.   {
  6.     public IEnumerator GetEnumerator()
  7.     {
  8.       return new CollectionEnumerator();
  9.     }
  10.   }
  11. }

Program.cs

  1. using System;
  2. namespace CollectionClassConcept
  3. {
  4.   class Program
  5.   {
  6.     static void Main(string[] args)
  7.     {
  8.       CollectionClass collectionClass=new CollectionClass();
  9.       foreach (string  collection in collectionClass)
  10.       {
  11.         Console.WriteLine(collection);
  12.       }
  13.     }
  14.   }
  15. }

When we run the Application, we see no compile or run time error, but the console window shows endless lines repeating them in a loop, as shown below.

C#

What causes this endless output to come from? Let’s try to find out. From the CollectionClass class, GetEnumerator method is called from the foreach loop. Here it is expecting this GetEnumerator method to return something that is IEnumerator so that iteration or enumeration can happen. Thereafter it invokes the MoveNext method from the returned instance of CollectionEnumerator class, so in case MoveNext is returning a true value, that means implicitly that there is some data that could be read, and in turn it calls the Current property to get that data. When Current property is called, it writes “Inside Current Property” and the get accessor of the property always returns “Current Property” text because we mentioned that in the code. Now again MoveNext is called and as per our defined case, again true is returned, so it happens to be an endless loop now. In case MoveNext returns false stating that no more data is left, the loop will stop there. Let’s try to do this in next step.

Step 5

CollectionEnumerator

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. namespace CollectionClassConcept
  5. {
  6.   public class CollectionEnumerator : IEnumerator
  7.   {
  8.     public List<string> StringList=new List<string>(4) { “Value One”“Value Two”“Value Three”“Value Four”“Value Five” };
  9.     public int Counter = -1;
  10.     public bool MoveNext()
  11.     {
  12.       Counter++;
  13.       Console.WriteLine(“Inside MoveNext Method : “ + Counter);
  14.       return Counter != 5;
  15.     }
  16.     public void Reset()
  17.     {
  18.       Console.WriteLine(“Inside Reset Method”);
  19.     }
  20.     public object Current
  21.     {
  22.       get
  23.       {
  24.         Console.WriteLine(“Inside Current Property : “ + StringList[Counter]);
  25.         return StringList[Counter];
  26.       }
  27.     }
  28.   }
  29. }

CollectionClass

  1. using System.Collections;
  2. namespace CollectionClassConcept
  3. {
  4.   public class CollectionClass
  5.   {
  6.     public IEnumerator GetEnumerator()
  7.     {
  8.       return new CollectionEnumerator();
  9.     }
  10.   }
  11. }

Program.cs

  1. using System;
  2. namespace CollectionClassConcept
  3. {
  4.   class Program
  5.   {
  6.     static void Main(string[] args)
  7.     {
  8.       CollectionClass collectionClass=new CollectionClass();
  9.       foreach (string  collection in collectionClass)
  10.       {
  11.         Console.WriteLine(collection);
  12.       }
  13.     }
  14.   }
  15. }

Output

C#

In the CollectionEnumerator class, a generic List of strings is created. One can also create an array or ArrayList. In this case, we are using List having 5 members with values: “Value One”, “Value Two”, “Value Three”, “Value Four”, and “Value Five”. There is a counter variable initialized to -1. Every time MoveNext method is called, the counter value is incremented by 1, now we specifically specified the length of the list as 5 and in the MoveNext method we specify to return false if the counter exceeds 5. Therefore the counter will have a track on how many times the MoveNext method is called. Whenever MoveNext method returns true, the Current property gets called that returns the string member from the defined list i.e. StringList indexed at the current value of the counter. In the similar way one can iterate through the list based on the length of the list or array.

Step 6

Let’s now try making the collection class independent of fixed length for e.g. we used 5 in the previous example. So we’ll do the implementation to dynamically take the input and iterate through it via calculating its length and accordingly process and show the output.

CollectionEnumerator

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. namespace CollectionClassConcept
  6. {
  7.   public class CollectionEnumerator : IEnumerator
  8.   {
  9.     public List<string> StringList;
  10.     public int Counter = -1;
  11.     public CollectionEnumerator(string parameter)
  12.     {
  13.       StringList = parameter.Split(‘ ‘).ToList();
  14.     }
  15.     public bool MoveNext()
  16.     {
  17.       Counter++;
  18.       Console.WriteLine(“Inside MoveNext Method : “ + Counter);
  19.       return Counter != StringList.Count;
  20.     }
  21.     public void Reset()
  22.     {
  23.       Console.WriteLine(“Inside Reset Method”);
  24.     }
  25.     public object Current
  26.     {
  27.       get
  28.       {
  29.         Console.WriteLine(“Inside Current Property : “ + StringList[Counter]);
  30.         return StringList[Counter];
  31.       }
  32.     }
  33.   }
  34. }

CollectionClass

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. namespace CollectionClassConcept
  4. {
  5.   public class CollectionClass
  6.   {
  7.     private string _parameter;
  8.     public CollectionClass(string parameter)
  9.     {
  10.       _parameter = parameter;
  11.     }
  12.     public IEnumerator GetEnumerator()
  13.     {
  14.       return new CollectionEnumerator(_parameter);
  15.     }
  16.   }
  17. }

Program.cs

  1. using System;
  2. using System.Security.AccessControl;
  3. namespace CollectionClassConcept
  4. {
  5.   class Program
  6.   {
  7.     static void Main(string[] args)
  8.     {
  9.       CollectionClass collectionClass=new CollectionClass(“We know what is a collection class.”);
  10.       foreach (string  collection in collectionClass)
  11.       {
  12.         Console.WriteLine(collection);
  13.       }
  14.       Console.ReadLine();
  15.     }
  16.   }
  17. }

The explanation of the above written code is very straight forward and self-explanatory. In the main method of Program class, when we try to create an instance of CollectionClass we pass string as a parameter keeping in mind that the class has a parameterized constructor that takes one string argument. So CollectionClass constructor being called first holds the parameter in variable _parameter. Now, the foreach statement invokes GetEnumerator which in turn creates an instance of CollectionEnumerator class and pass _parameter as a parameter to the constructor of CollectionEnumerator keeping in mind that CollectionEnumerator class now also has a parameterized constructor taking one string argument. As per the implementation of CollectionEnumerator class, as soon as its constructor is called, the parameter is split by a space character via Split method of strings. One can also supply more split character options by providing an array of splitters to the split method here, for now we’ll use space as a splitter. We convert the array that we got after split to a List of string and initialize that to StringList list defined in the class. Now to make the enumeration work independently of the fixed length, we’ll not use a constant number for the length but the length of the list that we have got in the constructor. So in the MoveNext method it returns false only if the counter does not match the count of the list. So at the end we have a collection class that is custom as per our implementation and iterates through the string on the words inside. One can also customize it further to match the requirement.

Conclusion

This article covered the topic of Collection Class/ Collection Object in detail. Creating a collection class may sound a bit tough but it’s not that tough, and it helps us to have full control over the kind of enumeration we want to use. Happy Coding.

Source Code on Github

Source Code

Other Series

My series of articles

Read more

For more technical articles, you can reach out to CodeTeddy.

Advertisements

Author: Akhil Mittal

Akhil Mittal is a Microsoft MVP,C# Corner MVP, Code project MVP, blogger, programmer by heart and currently working as a Sr. Analyst in Magic Software and have an experience of more than 9 years in C#.Net. He holds a B.Tech in Computer Science and hold a diploma in Information Security and Application Development. His work experience includes Development of Enterprise Applications using C#,.Net and Sql Server,Analysis, Product Management as well as Research and Development. He is a MCP in Web Applications(MCTS-70-528,MCTS-70-515) and .Net Framework 2.0 (MCTS-70-536).