Sunday, June 21, 2026

C# Example of class that Implements generic ICollection interface

Step1. Create a generic class named MyCollection<T> which implements ICollection<T> interface. This interface has following members:
  • int Count { get; }
  • bool IsReadOnly { get; }
  • void Add(T item);
  • void Clear();
  • bool Contains(T item);
  • void CopyTo(T[] array, int arrayIndex);
  • bool Remove(T item);
To implement these properties and methods, first we create a generic type array _items that is initialized inside constructor of MyCollection<T>. The implementing methods and properties work on this array e.g.
  • to add a new item to the array, 
  • remove an item from the array, 
  • clear all items from the array, 
  • check if an item exists in the array, 
  • count the total number of items in the array etc.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

class MyCollection<T> : ICollection<T>
{
    private T[] _items;

    public MyCollection(T[] items)
    {
        // Avoid null references if items is passed as null
        _items = items ?? new T[0];
    }

    public int Count => _items.Length;

    public bool IsReadOnly => false;

    public void Add(T item)
    {
        T[] newItems = new T[_items.Length + 1];
        for (int i = 0; i < _items.Length; i++)
        {
            newItems[i] = _items[i];
        }
        
        // FIX: The last index is Length, not Length + 1
        newItems[_items.Length] = item; 
        _items = newItems;
    }

    public void Clear()
    {
        _items = new T[0];
    }

    public bool Contains(T item)
    {
        // FIX: FirstOrDefault with a predicate or EqualityComparer is required for generics.
        // It's cleaner to use Array.IndexOf or LINQ's Contains.
        return _items.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        if (array == null)
            throw new ArgumentNullException(nameof(array));

        if (arrayIndex < 0)
            throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Index cannot be negative.");

        if (array.Length - arrayIndex < _items.Length)
            throw new ArgumentException("The destination array is not large enough.");

        Array.Copy(_items, 0, array, arrayIndex, _items.Length);
    }

    public bool Remove(T item)
    {
        // FIX: Locate the item's index safely across any generic type
        int index = Array.IndexOf(_items, item);
        if (index < 0) return false; // Item not found

        T[] newItems = new T[_items.Length - 1];
        
        // Copy elements before the removed item
        Array.Copy(_items, 0, newItems, 0, index);
        // Copy elements after the removed item
        Array.Copy(_items, index + 1, newItems, index, _items.Length - index - 1);
        
        _items = newItems;
        return true;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return new MyEnumerator<T>(_items);
    }

    // FIX: Standard boilerplate implementation for IEnumerable
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}
Step2. Create MyEnumerator<T> class which implements IEnumerator<T>. This code uses the logic already explained in the post: Example of a class that Implements generic IEnumerable<T> interface
class MyEnumerator<T> : IEnumerator<T>
{
    private T[] _items;
    private int _index = -1;

    public MyEnumerator(T[] items)
    {
        _items = items;
    }

    public T Current
    {
        get
        {
            if (_index < 0 || _index >= _items.Length)
            {
                throw new InvalidOperationException("Enumeration has either not started or has already finished.");
            }
            return _items[_index];
        }
    }

    // FIX: Explicitly implement the legacy non-generic Current property
    object IEnumerator.Current => this.Current;

    public bool MoveNext()
    {
        _index++;
        return _index < _items.Length;
    }

    public void Reset()
    {
        _index = -1;
    }

    // FIX: Must implement Dispose even if empty for IEnumerator<T>
    public void Dispose()
    {
        // No unmanaged resources to clean up, leave empty.
    }
}
Step3. Create Program class as consumer of MyCollection<T>
class Program
{
    static void Main(string[] args)
    {
        string[] colors = { "red", "green", "blue", "pink" };
        MyCollection<string> mycollection = new MyCollection<string>(colors);
        Console.WriteLine("Use properties and methods of MyCollection<string>");
        Console.WriteLine($"Total items is colors: {mycollection.Count}");
        Console.WriteLine($"Does yellow color exists in colors: {mycollection.Contains("yellow")}");
        // Add yellow color
        mycollection.Add("yellow");
        Console.WriteLine($"Does yellow color exists in colors: {mycollection.Contains("yellow")}");
        // Remove blue color
        var result = mycollection.Remove("blue") ? "Blue color removed" : "Blue color not found";
        Console.WriteLine(result);
    }
}
OUTPUT
Use properties and methods of MyCollection<string>
Total items is colors: 4
Does yellow color exists in colors: True
Does yellow color exists in colors: True
Blue color removed

No comments:

Post a Comment

Hot Topics