Wednesday 20 May 2015

Custom Collection with an Indexer

Let's say that for some reason you'd like to create your own collection by inheriting the CollectionBase Class. This will give you a starting point, and leverage existing collection functionality in a consistent way.

We are also going create an indexer for our custom collection 

In C#, indexers are referred to as smart arrays. A C# indexer is a class property that allows you to access a member variable of that class or struct similarly to what you would do with an index of an array. In C#, indexers are created using this keyword. Indexers in can be used in both classes and structs.

in our case we'll be able to access our custom collection members using an index.

        //access our data using an indexer
        Console.WriteLine(personCollection[1]);

As always, let's start by create our console application with a main function.

dotnet new console -n pav.customCollection --use-program-main

next let's open our newly created application in ms code

code ./pav.customCollection

your terminal could look something like this


We're going to reuse our person class, in which we create people using their name and birthdate

Then we are going to create our custom collection and we are just going to wrap our basic functions, but for example if you wanted to you could modify the inputs, or do whatever else you'd like
We'll also create an indexer for our custom collection, this will let us use integers to access our list items, but this could have been implemented using keys.


using System.Collections;

namespace pav.customCollection;

class Person : IComparable<Person>
{
    public string Name { get; set; }
    public DateTime BirthDate { get; set; }
    public Person(string Name, DateTime BirthDate)
    {
        this.Name = Name;
        this.BirthDate = BirthDate;
    }
    public int GetAge()
    {
        DateTime today = DateTime.Today;
        int age = today.Year - BirthDate.Year;
        return BirthDate > today.AddYears(-age) ? --age : age;
    }
    public override string ToString() { return $"{Name} is {GetAge()}"; }
    public int CompareTo(Person? other)
    {
        if (other != null)
            return GetAge() - other.GetAge();
        return 0;
    }
}

class PersonCollection : CollectionBase
{
    public void Add(Person person)
    {
        base.List.Add(person);
    }
    public void Insert(int index, Person person)
    {
        base.List.Insert(index, person);
    }
    public void Remove(Person person)
    {
        base.List.Remove(person);
    }

    //our indexer
    public Person this[int index]
    {
        get {
            var p = base.List[index] as Person;

            if(p != null)
                return p;
            throw new NullReferenceException($"no member found at index: {index}");
            }
        set { base.List[index] = value; }
    }
}
class Program
{

    static void Main(string[] args)
    {
        var personCollection = new PersonCollection();

        personCollection.Add(new Person("Pavel", new DateTime(1984, 01, 31)));
        personCollection.Add(new Person("Tomek", new DateTime(1988, 08, 28)));
        personCollection.Add(new Person("Magda", new DateTime(1984, 06, 28)));

        foreach (Person p in personCollection)
            Console.WriteLine(p.ToString());

        Console.WriteLine();
       
        //access our data using an indexer
        Console.WriteLine(personCollection[1]);
        personCollection[1] = new Person("marin", new DateTime(1983, 11,2));
        Console.WriteLine(personCollection[1]);
    }
}


This base class gives us the basics of what's needed to implement our own collection and we added the indexer for good measure.


Notice that our indexer let's us read and write to our list items using and index.

       
//access our data using an indexer
        Console.WriteLine(personCollection[1]);
        personCollection[1] = new Person("marin", new DateTime(1983, 11,2));
        Console.WriteLine(personCollection[1]);