Click here to Skip to main content
15,303,572 members
Articles / Programming Languages / C#
Article
Posted 4 Apr 2017

Stats

30.4K views
478 downloads
14 bookmarked

Learning C#: Custom Collection Classes in C#

Rate me:
Please Sign up or sign in to vote.
4.88/5 (13 votes)
4 Apr 2017CPOL6 min read
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.

Image 1

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.

Image 2

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

C#
namespace CollectionClassConcept
{
  public class CollectionClass
  {

  }
}

Program.cs

C#
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.

Image 3

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

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

Program.cs

C#
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.

Image 4

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

C#
using System.Collections;

namespace CollectionClassConcept
{
  public class CollectionEnumerator : IEnumerator
  {
  }
}

CollectionClass

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

Program.cs

C#
using System;

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

Image 5

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

C#
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

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

Program.cs

C#
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.

Image 6

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

C#
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

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

Program.cs

C#
using System;

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

Output

Image 7

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

C#
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

C#
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

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Akhil Mittal
Architect https://codeteddy.com/
India India
Akhil Mittal is an Ex-Microsoft MVP(Most Valuable Professional), C# Corner MVP, Codeproject MVP, a blogger, author and likes to write/read technical articles. Akhil has an experience of around 12 years in developing, designing, architecting enterprises level applications primarily in Microsoft Technologies. Akhil enjoys working on technologies like MVC, Web API, Entity Framework, Angular, C# and BlockChain. Akhil is an MCP( Microsoft Certified Professional) in Web Applications (MCTS-70-528, MCTS-70-515) and .Net Framework 2.0 (MCTS-70-536). Visit Akhil Mittal’s personal blog CodeTeddy for some good and informative articles.
LinkedIn: https://www.linkedin.com/in/akhilmittal/
Group type: Collaborative Group

775 members


Comments and Discussions

 
QuestionIEnumerable<T> Pin
Qwertie11-Apr-17 10:05
MemberQwertie11-Apr-17 10:05 
QuestionTry to keep it "standard" Pin
irneb5-Apr-17 0:28
Memberirneb5-Apr-17 0:28 
AnswerRe: Try to keep it "standard" Pin
Qwertie11-Apr-17 10:02
MemberQwertie11-Apr-17 10:02 
Perhaps he wanted to show that foreach can call GetEnumerator even in a class that doesn't implement IEnumerable. However, one really should implement IEnumerator and IEnumerator<T> and this article doesn't even show how to implement the modern IEnumerator<T> interface.
GeneralNicely Explained Pin
prashita gupta4-Apr-17 17:57
Memberprashita gupta4-Apr-17 17:57 
GeneralRe: Nicely Explained Pin
Akhil Mittal4-Apr-17 18:17
professionalAkhil Mittal4-Apr-17 18:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.