Enumerating Over a Custom Collection

Hello,

In this post I would like to do something really simple and show you how to implement an IEnumerable on a custom collection. Some of you may ask why would I need to implement this interface on my custom collection? Usually the answer has to do with using the foreach loop, which, without the IEnumerable interface won’t be available to the collection users.

Ok, so, let’s say you have a simple Loan class with the following definition:

internal sealed class Loan
    {
        public Loan(decimal rate, DateTime maturity, decimal amount)
        {
            if (rate > 0 && maturity > DateTime.Now && amount > 0)
            {
                this.rate = rate;
                this.maturity = maturity;
                this.amount = amount;
            }
        }

        public decimal Rate { get { return rate; } }
        public DateTime Maturity { get { return maturity; } }
        public decimal Amount { get { return amount; } }

        private decimal rate;
        private DateTime maturity;
        private decimal amount;
    }

You created the above Loan class to be able to see the total amount you need to repay by some future date. There is really nothing wrong with simply having an array of Loans (e.g. one for your car, another for your house) and using the foreach on the array. In other words, something like this:

class EntryPoint
    {
        static void Main(string[] args)
        {
            decimal totalToRepay = 0;
            Loan [] loans =  
            { 
                new Loan((decimal)0.03, DateTime.Parse("19/04/2016"), (decimal)15000),
                new Loan((decimal)0.018, DateTime.Parse("19/04/2016"), (decimal)260000)
            };

            foreach (Loan l in loans)
                totalToRepay += ((l.Maturity - DateTime.Now).Days / 365) * l.Rate * l.Amount + l.Amount;

            Console.WriteLine("{0} {1,2:C}", "You will need to repay an amount of ", totalToRepay);
            Console.ReadLine();

        }
    }

The above works for simple single-threaded use. However, as soon as you introduce a possibility of one thread updating the loans array while another thread is iterating over it, you need to implement some kind of synchronization mechanism. The loans array inherits the ICollection implementation from the Collections class. This interface has a SyncRoot property which allows to lock the variable. At this point you begin to realise that a simple array of loans may need to evolve into a custom collection with added synchronization benefit. You go ahead and implement a collection of loans class:

internal sealed class LoanCollection: IEnumerable
    {
        public LoanCollection(Loan[] loanArray)
        {
            loanCol = new Loan[loanArray.Length];
            for (int i=0; i< loanArray.Length; i++)
            {
                loanCol[i] = loanArray[i];
            }
        }

       public IEnumerator GetEnumerator()
       {
           lock (loanCol.SyncRoot)
           {
               for (int i = 0; i < loanCol.Length; i++)
                   yield return loanCol[i];
           }
       }

       private readonly Loan[] loanCol;
    }

Note how simple it is to synchronize with the lock statement. C# compiler will be busy expanding this statement into a proper Monitor.Enter and Monitor.Exit block. Also note that for a non-generic IEnumerable we only need to implement the GetEnumerator method. So, are we done here? Not quite, as I always like to point out alternative and sometimes simpler methods for doing the same things. Instead of separating the enumeration and the synchronization, we could attempt to get both of these features in one go. To do this, we can inherit from a thread-safe System.Collections.Concurrent.ConcurrentBag and simplify the LoanCollection class a little bit. The ConcurrentBag is a generic collection, so we will restrict the type to Loan class only:

internal sealed class LoanCollectionSync<T> : System.Collections.Concurrent.ConcurrentBag<T> where T : Loan
    {
        public LoanCollectionSync(T[] loanArray)
        {
            foreach (T l in loanArray)
            {
                Add(l);
            }
        }
    }

How cool is that? Not only we get the IEnumerable from the ConcurrentBag, but the collection is now thread-safe as well! There is a special TryPeek method in the ConcurrentBag that can be used to iterate over the collections. The built-in enumerator is also thread-safe (i.e. represents a snapshot of a collection in the moment in time before any updates), so, we can still use the same iteration approach as before:

LoanCollectionSync<Loan> syncLoans = new LoanCollectionSync<Loan>(loans);

   foreach (Loan l in syncLoans)
      totalToRepay += ((l.Maturity - DateTime.Now).Days / 365) * l.Rate * l.Amount + l.Amount;
   
   Console.WriteLine("{0} {1}", "You will need to repay an amount of ", totalToRepay.ToString("C"));
   Console.ReadLine();

Thanks for reading and have a nice day!

Advertisements
This entry was posted in C# Programming and tagged . Bookmark the permalink.