Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Learning C#: Custom Collection Classes in C#

0.00/5 (No votes)
4 Apr 2017 1  
This article will explain how one can create their own custom collection class that can be iterated through.

Table of Contents

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 how one can create their own custom collection class that can be iterated through. The article follows a 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#. The article uses a console application named CollectionClassConcept that contains an empty class named CollectionClass. We’ll use this initial setup to learn about collection classes.

Step 1

Considering that we already have a collection class (even if it is empty for now) try to access the CollectionClass in the Main method of Program.cs; create an instance of the class and iterate through it.

CollectionClass

namespace CollectionClassConcept
{
  public class CollectionClass
  {

  }
}

Program.cs

using System;

namespace CollectionClassConcept
{
  class Program
  {
    static void Main(string[] args)
    {
      CollectionClass collectionClass=new CollectionClass();
      foreach (string  collection in collectionClass)
      {
        Console.WriteLine(collection);
      }
    }
  }
}

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

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. So add a new GetEnumerator() method into the CollectionClass class.

CollectionClass

namespace CollectionClassConcept
{
  public class CollectionClass
  {
    public int GetEnumerator()
    {
      
    }
  }
}

Program.cs

using System;

namespace CollectionClassConcept
{
  class Program
  {
    static void Main(string[] args)
    {
      CollectionClass collectionClass=new CollectionClass();
      foreach (string  collection in collectionClass)
      {
        Console.WriteLine(collection);
      }
    }
  }
}

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

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 above, foreach here makes an attempt to execute the GetEnumertaor method, but fails because it expects a MoveNext method and a property named Current, and it also expects the method to return any other value that integer.

Step 3

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

CollectionEnumerator

using System.Collections;

namespace CollectionClassConcept
{
  public class CollectionEnumerator : IEnumerator
  {
  }
}

CollectionClass

using System.Collections;
namespace CollectionClassConcept
{
  public class CollectionClass
  {
    public IEnumerator GetEnumerator()
    {
      return new CollectionEnumerator();
    }
  }
}

Program.cs

using System;

namespace CollectionClassConcept
{
  class Program
  {
    static void Main(string[] args)
    {
      CollectionClass collectionClass=new CollectionClass();
      foreach (string  collection in collectionClass)
      {
        Console.WriteLine(collection);
      }
    }
  }
}

Error:

  • 'CollectionEnumerator' does not implement interface member 'IEnumerator.Current'
  • 'CollectionEnumerator' does not implement interface member 'IEnumerator.MoveNext()'
  • 'CollectionEnumerator' does not implement interface member 'IEnumerator.Reset()'

So it is very clear that the IEnumerator interface (that the class CollectionEnumerator implements) has three methods that are needed to be implemented in the child class.

Step 4

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

CollectionEnumerator

using System.Collections;

namespace CollectionClassConcept
{
  public class CollectionEnumerator : IEnumerator
  {
    public bool MoveNext()
    {
      System.Console.WriteLine("Inside MoveNext Method");
      return true;
    }

    public void Reset()
    {
      System.Console.WriteLine("Inside Reset Method");
    }

    public object Current
    {
      get
      {
        System.Console.WriteLine("Inside Current Property");
        return "Current Property";
      }
    }
  }
}

CollectionClass

using System.Collections;
namespace CollectionClassConcept
{
  public class CollectionClass
  {
    public IEnumerator GetEnumerator()
    {
      return new CollectionEnumerator();
    }
  }
}

Program.cs

using System;

namespace CollectionClassConcept
{
  class Program
  {
    static void Main(string[] args)
    {
      CollectionClass collectionClass=new CollectionClass();
      foreach (string  collection in collectionClass)
      {
        Console.WriteLine(collection);
      }
    }
  }
}

When we run the application, we see no compile or run time error, but the console windows shows endless lines repeating them in a loop as shown in the following image.

What causes this endless output? 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 then gets accessor of the property and 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

using System;
using System.Collections;
using System.Collections.Generic;

namespace CollectionClassConcept
{
  public class CollectionEnumerator : IEnumerator
  {
    public List<string> StringList=new List<string>(4) { "Value One", "Value Two", "Value Three", "Value Four", "Value Five" };
    public int Counter = -1;

    public bool MoveNext()
    {
      Counter++;
      Console.WriteLine("Inside MoveNext Method : " + Counter);
      return Counter != 5;
    }

    public void Reset()
    {
      Console.WriteLine("Inside Reset Method");
    }

    public object Current
    {
      get
      {
        Console.WriteLine("Inside Current Property : " + StringList[Counter]);
        return StringList[Counter];
      }
    }
  }
}

CollectionClass

using System.Collections;
namespace CollectionClassConcept
{
  public class CollectionClass
  {
    public IEnumerator GetEnumerator()
    {
      return new CollectionEnumerator();
    }
  }
}

Program.cs

using System;

namespace CollectionClassConcept
{
  class Program
  {
    static void Main(string[] args)
    {
      CollectionClass collectionClass=new CollectionClass();
      foreach (string  collection in collectionClass)
      {
        Console.WriteLine(collection);
      }
    }
  }
}

Output

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 five members with values: "Value One", "Value Two", "Value Three", "Value Four", and "Value Five". There is a counter variable initialized to -1. Every time the MoveNext method is called, the counter value is incremented by 1. Now we specifically specified the length of the list as five and in the MoveNext method we specify to return false if the counter exceeds five. Therefore, the counter will have a track 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 example, we used five 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

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

namespace CollectionClassConcept
{
  public class CollectionEnumerator : IEnumerator
  {
    public List<string> StringList;
    public int Counter = -1;

    public CollectionEnumerator(string parameter)
    {
      StringList = parameter.Split(' ').ToList();
    }

    public bool MoveNext()
    {
      Counter++;
      Console.WriteLine("Inside MoveNext Method : " + Counter);
      return Counter != StringList.Count;
    }

    public void Reset()
    {
      Console.WriteLine("Inside Reset Method");
    }

    public object Current
    {
      get
      {
        Console.WriteLine("Inside Current Property : " + StringList[Counter]);
        return StringList[Counter];
      }
    }
  }
}

CollectionClass

using System.Collections;
using System.Collections.Generic;

namespace CollectionClassConcept
{
  public class CollectionClass
  {
    private string _parameter;

    public CollectionClass(string parameter)
    {
      _parameter = parameter;
    }
    public IEnumerator GetEnumerator()
    {
      return new CollectionEnumerator(_parameter);
    }
  }
}

Program.cs

using System;
using System.Security.AccessControl;

namespace CollectionClassConcept
{
  class Program
  {
    static void Main(string[] args)
    {
      CollectionClass collectionClass=new CollectionClass("We know what is a collection class.");
      foreach (string  collection in collectionClass)
      {
        Console.WriteLine(collection);
      }
      Console.ReadLine();
    }
  }
}

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

 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here