Generics

The Buffer Problem

namespace DataStructures
{
    public class CircularBuffer
    {
        private double[] _buffer;
        public int _start;
        public int _end;
        public CircularBuffer(int capacity)
        {
            _buffer = new double[capacity + 1];
            _start = 0;
            _end = 0;
        }

        public void Write(double value)
        {
            _buffer[_end] = value;
            _end = (_end + 1) % _buffer.Length;
            if (_end == _start)
            {
                _start = (_start + 1) % _buffer.Length;
            }
        }

        public double Read()
        {
            var result = _buffer[_start];
            _buffer[_start] = 0.0;
            if (_start == _end)
            {
                return 0.0;
            }

            _start = (_start + 1) % _buffer.Length;

            return result;
        }
    }
}

Circular buffer is a data stucture where a size is assigned to it. It follows a queue. You start writing values to the buffer, it gets added at the end. You read values, it reads from the start. In case of overflow, the first value is flushed out, all the values shift in front to make room for the lates incoming value at the back. A Circular buffer is generally used to stream audio data.

Let's see a case of a buffer with a capacity of 3 elements. Notice the circular buffer we created only accepts a double. Upon reading the buffer, only last 3 values are fetched. On reading more it returns 0.0;


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            CircularBuffer cb = new CircularBuffer(capacity: 3);
            cb.Write(1);
            cb.Write(2);
            cb.Write(3);
            cb.Write(4);
            cb.Write(5);

            Console.WriteLine(cb.Read());
            Console.WriteLine(cb.Read());
            Console.WriteLine(cb.Read());

            //Reading this would result in a default value of a double i.e. 0.0
            Console.WriteLine(cb.Read());

            Console.ReadLine();
        }
    }
}

The Problem here is that it only accepts double. The Write method only accepts a double, and the Read method only returns a double. I want to use this Circular buffer for other datatypes like Byte, String etc. Let's see a couple of solutions in the next section that do not involve generics.

The Object Solution

What we are going to do is that in the Circular buffer class, we will replace all the double to object. So now, my buffer stores an array of objects, it Writes an object and Reads an object. Let's see how the code will look like.


namespace DataStructures
{
    public class CircularBuffer
    {
        private object[] _buffer;
        public int _start;
        public int _end;
        public CircularBuffer(int capacity)
        {
            _buffer = new object[capacity + 1];
            _start = 0;
            _end = 0;
        }

        public void Write(object value)
        {
            _buffer[_end] = value;
            _end = (_end + 1) % _buffer.Length;
            if (_end == _start)
            {
                _start = (_start + 1) % _buffer.Length;
            }
        }

        public object Read()
        {
            var result = _buffer[_start];
            _buffer[_start] = 0.0;
            if (_start == _end)
            {
                return 0.0;
            }

            _start = (_start + 1) % _buffer.Length;

            return result;
        }
    }
}

And not this allows me to Write String in the buffer. Basically it allows me to write anything(since every type has a base class of object). Let's see that in action. We will now enter String. Note, double still works.


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            CircularBuffer cb = new CircularBuffer(capacity: 3);
            cb.Write("1");
            cb.Write("2");
            cb.Write("3");
            cb.Write("4");
            cb.Write("5");

            Console.WriteLine(cb.Read());
            Console.WriteLine(cb.Read());
            Console.WriteLine(cb.Read());

            //Reading this would result in a default value of a double i.e. 0.0
            Console.WriteLine(cb.Read());

            Console.ReadLine();
        }
    }
}

Notice this works because Console.WriteLine knows how to implicitly convert a double to a String(This is what was happening earlier. Not a big deal!) just for the sake of displaying on the Console. And this time it is just displaying String itself.

Let's have a couple of different things in the buffer now, let's have a combination of String which is actually a small word that can not be converted to a double and a double value.


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            CircularBuffer cb = new CircularBuffer(capacity: 3);
            cb.Write("1");
            cb.Write("2");
            cb.Write("3");
            cb.Write("Hello World!");
            cb.Write("5");

            Console.WriteLine(cb.Read());
            Console.WriteLine(cb.Read());
            Console.WriteLine(cb.Read());

            //Reading this would result in a default value of a double i.e. 0.0
            Console.WriteLine(cb.Read());

            Console.ReadLine();
        }
    }
}

So far, so good. Notice the operation being done on the buffer here is just reading and writing. Console.WriteLine does a pretty good job here to convert everything to a String for displaying. Let's say we come back to our Writing of only some String values which can be converted to double. And this time we want to do some operation like addition on the buffer.


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            CircularBuffer cb = new CircularBuffer(capacity: 3);
            cb.Write("1");
            cb.Write("2");
            cb.Write("3");
            cb.Write("4");
            cb.Write("5");

            double sum = 0;
            for (int i = 0; i < 3; i++)
            {
                sum += cb.Read();
            }

            Console.WriteLine(sum);

            Console.ReadLine();
        }
    }
}

The compiler throws error here saying :

Ok, no problem, we will do a type conversion and the problem will be fixed.


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            CircularBuffer cb = new CircularBuffer(capacity: 3);
            cb.Write("1");
            cb.Write("2");
            cb.Write("3");
            cb.Write("4");
            cb.Write("5");

            double sum = 0;
            for (int i = 0; i < 3; i++)
            {
                sum += Convert.ToDouble(cb.Read());
            }

            Console.WriteLine(sum);

            Console.ReadLine();
        }
    }
}

There are some points to notice

  • It doesn't look correct that we have to convert the type everytime we want to use it. Why shouldn't the Read method return a double.
  • And since the buffer is an object array. Its possible to write anything in it. See the example above where we entered "Hello World!" in the buffer. Imagine some one doing a Sum operation on that and trying to convert "Hello World!" to a double!!. Of course that would throw an exception.
  • The object array will not be type safe.

There can be an argument upon let's check the value before we add it to the sum variable using value is double in an if condition. First, do we want to keep a lot of different types in a single memory location to use? Second, everytime we store something in the object array, that type is boxed and when read, it is unboxed. Boxing and Unboxing requires separate allocation of memory in the managed heap and it makes our programs slower(obviously because there are extra steps to work with a value, which could have been much easier when using them as their direct types.)

Copy and Paste Solution

All right so we need it for String too. No Problem, just copy paste the code for double and create a new class.


namespace DataStructures
{
    public class CircularBufferString
    {
        private String[] _buffer;
        public int _start;
        public int _end;
        public CircularBufferString(int capacity)
        {
            _buffer = new String[capacity + 1];
            _start = 0;
            _end = 0;
        }

        public void Write(String value)
        {
            _buffer[_end] = value;
            _end = (_end + 1) % _buffer.Length;
            if (_end == _start)
            {
                _start = (_start + 1) % _buffer.Length;
            }
        }

        public String Read()
        {
            var result = _buffer[_start];
            _buffer[_start] = 0.0;
            if (_start == _end)
            {
                return 0.0;
            }

            _start = (_start + 1) % _buffer.Length;

            return result;
        }
    }
}

Here we go, we have it for strings too. But wait, i also wanted it for int!! How many times are you going to copy and paste??

If you notice the above code, we just changed the Type double to String (forget the class name changing)

.

What if we could have a placeholder like T instead of double or String. See an example below:


namespace DataStructures
{
    public class CircularBufferString
    {
        private T[] _buffer;
        public int _start;
        public int _end;
        public CircularBufferString(int capacity)
        {
            _buffer = new String[capacity + 1];
            _start = 0;
            _end = 0;
        }

        public void Write(T value)
        {
            _buffer[_end] = value;
            _end = (_end + 1) % _buffer.Length;
            if (_end == _start)
            {
                _start = (_start + 1) % _buffer.Length;
            }
        }

        public T Read()
        {
            var result = _buffer[_start];
            _buffer[_start] = 0.0;
            if (_start == _end)
            {
                return 0.0;
            }

            _start = (_start + 1) % _buffer.Length;

            return result;
        }
    }
}

Notice that we are creating a type T when declaring the array, when Writing and when Reading. We want to declare this type T sometime later after compilation.

Generics

The idea of Generics originated because we wanted to reuse some code that is needed by different types. And we don't want object that can be used instead of any type. We want the Type itself. In a type safe manner. So we write a code:

  • That can defer the type specification to the client who uses that code.
  • The Internal algorithms of operation are independent of the Type. For example the logic for enumerating over a list of int or string should be same.

This is how it looks:


public class CircularBuffer<T>
{
    private T[] _buffer;

    //...
}

Notice CircularBuffer<T>. This is the constructor for a class definition. <T> is brackets accepting a list of parameters. We CAN have multiple parameters being passed here. These parameters are nothing but a Type. T for Type. CircularBuffer<T> is now called CircularBuffer of T

A Generic Circular Buffer

namespace DataStructures
{
    public class CircularBuffer<T>
    {
        private T[] _buffer;
        public int _start;
        public int _end;
        public CircularBuffer(int capacity)
        {
            _buffer = new T[capacity + 1];
            _start = 0;
            _end = 0;
        }

        public void Write(T value)
        {
            _buffer[_end] = value;
            _end = (_end + 1) % _buffer.Length;
            if (_end == _start)
            {
                _start = (_start + 1) % _buffer.Length;
            }
        }

        public T Read()
        {
            var result = _buffer[_start];
            _start = (_start + 1) % _buffer.Length;

            return result;
        }
    }
}


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            var cb = new CircularBuffer<string>(capacity: 3);
            cb.Write("Hello");
            cb.Write("I");
            cb.Write("Am");
            cb.Write("Suyash");

            Console.WriteLine(cb.Read());
            Console.WriteLine(cb.Read());
            Console.WriteLine(cb.Read());


            Console.ReadLine();
        }
    }
}

Let's proove that it is type safe. Try to insert another type and see it throw a compiler error


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            var cb = new CircularBuffer<string>(capacity: 3);
            cb.Write("Hello");
            cb.Write("I");
            cb.Write("Am");
            cb.Write("Suyash");
            cb.Write(1);

            Console.WriteLine(cb.Read());
            Console.WriteLine(cb.Read());
            Console.WriteLine(cb.Read());


            Console.ReadLine();
        }
    }
}

But change it to a Circular buffer of int, change all the values to int and see it work.


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            var cb = new CircularBuffer<int>(capacity: 3);
            cb.Write(1);
            cb.Write(2);
            cb.Write(3);
            cb.Write(4);
            cb.Write(5);

            Console.WriteLine(cb.Read());
            Console.WriteLine(cb.Read());
            Console.WriteLine(cb.Read());


            Console.ReadLine();
        }
    }
}

Terminology
  • A class with ended with angle brackets is called a Generic Class
  • You can also have Generic Interfaces, Generic Delegates, Generic Structs.
  • T is what we call a Type Parameter
  • T is just conventional you can use X. Basically any name.
  • You can have multiple Type Parameter
  • var buffer = new CircularBuffer<double>();. Here double is the Type Argument. Only a double can be used for this.
  • When we create Generic Type, passing in a Type Argument, there is a new class definition thats created behind the scenes and ready to be used. CircularBuffer<double> is a new Type available to use.
Generic Collections

Generics and Collections work well together because collection is all about managing and organizing data and quite often you want to organize a specific type of data like keep a list of employee objects around and the list represents a department, a department of employees. Different collections have different weaknesses and capabilities so its good to know about all the options

  • Array
  • List, Capacity
  • Queue, Enqueue, Dequeue, Peek, Contains
  • Stack, Push, Pop
  • LinkedList
  • Dictionary
  • SortedList, SortedDictionary, SortedSet
  • Concurrent Collections : Multiple writers and readers, thread safe
  • Immutable Collections : Thread safe, modifications produce new collections
Generic Classes & Interfaces
Let's refactor our Circular buffer a little. Here is the code:

namespace DataStructures
{
    public class CircularBuffer<T>
    {
        private T[] _buffer;
        public int _start;
        public int _end;
        public CircularBuffer(int capacity)
        {
            _buffer = new T[capacity + 1];
            _start = 0;
            _end = 0;
        }

        public void Write(T value)
        {
            _buffer[_end] = value;
            _end = (_end + 1) % _buffer.Length;
            if (_end == _start)
            {
                _start = (_start + 1) % _buffer.Length;
            }
        }

        public T Read()
        {
            var result = _buffer[_start];
            _start = (_start + 1) % _buffer.Length;

            return result;
        }

        public int Capacity
        {
            get { return _buffer.Length; }
        }

        public bool IsEmpty
        {
            get { return _end == _start; }
        }

        public bool IsFull
        {
            get { return (_end + 1) % _buffer.Length == _start; }
        }
    }
}


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            var buffer = new CircularBuffer<double>(capacity: 3);


            ProcessInput(buffer);
            ProcessBuffer(buffer);


            Console.ReadLine();
        }

        private static void ProcessBuffer(CircularBuffer<double> buffer)
        {
            var sum = 0.0;
            Console.WriteLine("Buffer: ");
            while (!buffer.IsEmpty)
            {
                sum += buffer.Read();
            }
            Console.WriteLine(sum);
        }

        private static void ProcessInput(CircularBuffer<double> buffer)
        {
            while (true)
            {
                var value = 0.0;
                var input = Console.ReadLine();

                if (double.TryParse(input, out value))
                {
                    buffer.Write(value);
                    continue;
                }
                break;
            }
        }
    }
}

There can be multiple types of such buffers. Let's refactor it out to an interface.


namespace DataStructures
{
    public interface IBuffer<T>
    {
        bool IsEmpty { get; }
        void Write(T value);
        T Read();
    }


    public class CircularBuffer<T> : IBuffer<T>
    {
        private T[] _buffer;
        public int _start;
        public int _end;
        public CircularBuffer(int capacity)
        {
            _buffer = new T[capacity + 1];
            _start = 0;
            _end = 0;
        }

        public void Write(T value)
        {
            _buffer[_end] = value;
            _end = (_end + 1) % _buffer.Length;
            if (_end == _start)
            {
                _start = (_start + 1) % _buffer.Length;
            }
        }

        public T Read()
        {
            var result = _buffer[_start];
            _start = (_start + 1) % _buffer.Length;

            return result;
        }

        public int Capacity
        {
            get { return _buffer.Length; }
        }

        public bool IsEmpty
        {
            get { return _end == _start; }
        }

        public bool IsFull
        {
            get { return (_end + 1) % _buffer.Length == _start; }
        }
    }
}

And the Program


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            var buffer = new CircularBuffer<double>(capacity: 3);


            ProcessInput(buffer);
            ProcessBuffer(buffer);


            Console.ReadLine();
        }

        private static void ProcessBuffer(IBuffer<double> buffer)
        {
            var sum = 0.0;
            Console.WriteLine("Buffer: ");
            while (!buffer.IsEmpty)
            {
                sum += buffer.Read();
            }
            Console.WriteLine(sum);
        }

        private static void ProcessInput(IBuffer<double> buffer)
        {
            while (true)
            {
                var value = 0.0;
                var input = Console.ReadLine();

                if (double.TryParse(input, out value))
                {
                    buffer.Write(value);
                    continue;
                }
                break;
            }
        }
    }
}

Now i can use some other buffer instead of Circular Buffer. Let's implement it. and let's circular buffer implement the buffer


using System.Collections.Generic;
namespace DataStructures
{
    public interface IBuffer<T>
    {
        bool IsEmpty { get; }
        void Write(T value);
        T Read();
    }


    public class Buffer<T> : IBuffer<T>
    {

        public Queue<T> _queue = new Queue<T>();

        public virtual bool IsEmpty
        {
            get { return _queue.Count == 0; }
        }

        public virtual void Write(T value)
        {
            _queue.Enqueue(value);
        }

        public virtual T Read()
        {
            return _queue.Dequeue();
        }
    }


    public class CircularBuffer<T> : Buffer<T>
    {
        int _capacity;
        public CircularBuffer(int capacity = 10)
        {
            _capacity = capacity;
        }

        public override void Write(T value)
        {
            base.Write(value);
            if (_queue.Count > _capacity)
            {
                _queue.Dequeue();
            }
        }

        public bool IsFull { get { return _queue.Count == _capacity; } }
    }
}

IEnumerable<T>

IEnumerable, IComparer, IEqualityComparer

Generic Methods


using System.Collections.Generic;
using System.ComponentModel;
namespace DataStructures
{
    public interface IBuffer<T>: IEnumerable<T>
    {
        bool IsEmpty { get; }
        void Write(T value);
        IEnumerable<TOutput> AsEnumerableOf<TOutput>();

        T Read();
    }


    public class Buffer<T> : IBuffer<T>
    {

        public Queue<T> _queue = new Queue<T>();

        public virtual bool IsEmpty
        {
            get { return _queue.Count == 0; }
        }

        public virtual void Write(T value)
        {
            _queue.Enqueue(value);
        }

        public virtual T Read()
        {
            return _queue.Dequeue();
        }

        public IEnumerator<T> GetEnumerator()
        {
            return _queue.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }


        public IEnumerable<TOutput> AsEnumerableOf<TOutput>()
        {
            var converter = TypeDescriptor.GetConverter(typeof(T));
            foreach (var item in _queue)
            {
                var result = converter.ConvertTo(item, typeof(TOutput));
                yield return (TOutput)result;
            }
        }
    }


    public class CircularBuffer<T> : Buffer<T>
    {
        int _capacity;
        public CircularBuffer(int capacity = 10)
        {
            _capacity = capacity;
        }

        public override void Write(T value)
        {
            base.Write(value);
            if (_queue.Count > _capacity)
            {
                _queue.Dequeue();
            }
        }

        public bool IsFull { get { return _queue.Count == _capacity; } }
    }
}

On using it, the types which can be converted to each other normally will work fine. But for example if you try to convert a double to a DateTime, an exception is thrown.

A Generic method need not be a part of a Generic class. It can be a part of a Non-Generic Type

Let's move the AsEnumerableOf method in an extension method.


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

namespace DataStructures
{
    public static class BufferExtensions
    {
        public static IEnumerable<TOutput> AsEnumerableOf<T, TOutput>(this IBuffer<T> buffer)
        {
            var converter = TypeDescriptor.GetConverter(typeof(T));
            foreach (var item in buffer)
            {
                TOutput result = (TOutput)converter.ConvertTo(item, typeof(TOutput));
                yield return result;
            }
        }
    }
}

Notice the Class Type parameter is now passed in the Generic Method signature. T

A Type parameter is a part of Generic method signature, if you have another method with the same name and same number of type parameter, the compiler will throw error.

In case of extension methods, if there is just one parameter, no need to supply the generic type parameter. It will automatically infer.


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void ConsoleWrite(object data)
        {
            Console.WriteLine(data);
        }
        static void Main(string[] args)
        {
            //var buffer = new CircularBuffer<double>(capacity: 3);
            var buffer = new DataStructures.Buffer<double>();

            ProcessInput(buffer);

            Printer consoleOut = new Printer(ConsoleWrite);
            buffer.Dump(consoleOut);
            //buffer.Dump<double>(consoleOut);
            Console.WriteLine("---------");

            var resultInt =  buffer.AsEnumerableOf<double, int>();
            foreach (var item in resultInt)
            {
                Console.WriteLine(item);
            }

            ProcessBuffer(buffer);


            Console.ReadLine();
        }

        private static void ProcessBuffer(IBuffer<double> buffer)
        {
            var sum = 0.0;
            Console.WriteLine("Buffer: ");
            while (!buffer.IsEmpty)
            {
                sum += buffer.Read();
            }
            Console.WriteLine(sum);
        }

        private static void ProcessInput(IBuffer<double> buffer)
        {
            while (true)
            {
                var value = 0.0;
                var input = Console.ReadLine();

                if (double.TryParse(input, out value))
                {
                    buffer.Write(value);
                    continue;
                }
                break;
            }
        }
    }
}

Above example will now be converted to use a generic delegate. Instead of a Printer using an object type, lets defer the type.

Everyday Delegates
  • Func
  • Action
  • Predicate

Let's look at Action first. It allows to type a variable that will be a delegate so it points to a method, but that method has to return void. An Action delegate always returns void, but it can take from 0-16 parameters.Those parameters are 0-16 generic Type Arguments. For example Action<double> type delegate can only take a method that takes 1 parameter which is of the type double and it has to return void.


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

namespace DataStructures
{
    public static class BufferExtensions
    {
        public static void Dump<T>(this IBuffer<T> buffer, Action<T> print)
        {
            foreach (var item in buffer)
            {
                print(item);
            }
        }
        public static IEnumerable<TOutput> AsEnumerableOf<T, TOutput>(this IBuffer<T> buffer)
        {
            var converter = TypeDescriptor.GetConverter(typeof(T));
            foreach (var item in buffer)
            {
                TOutput result = (TOutput)converter.ConvertTo(item, typeof(TOutput));
                yield return result;
            }
        }
    }
}


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void ConsoleWrite(double data)
        {
            Console.WriteLine(data);
        }
        static void Main(string[] args)
        {
            //var buffer = new CircularBuffer<double>(capacity: 3);
            var buffer = new DataStructures.Buffer<double>();

            ProcessInput(buffer);

            Action<double> print = Console.WriteLine;
            buffer.Dump(print);
            //buffer.Dump<double>(consoleOut);
            Console.WriteLine("---------");

            var resultInt =  buffer.AsEnumerableOf<double, int>();
            foreach (var item in resultInt)
            {
                Console.WriteLine(item);
            }

            ProcessBuffer(buffer);


            Console.ReadLine();
        }

        private static void ProcessBuffer(IBuffer<double> buffer)
        {
            var sum = 0.0;
            Console.WriteLine("Buffer: ");
            while (!buffer.IsEmpty)
            {
                sum += buffer.Read();
            }
            Console.WriteLine(sum);
        }

        private static void ProcessInput(IBuffer<double> buffer)
        {
            while (true)
            {
                var value = 0.0;
                var input = Console.ReadLine();

                if (double.TryParse(input, out value))
                {
                    buffer.Write(value);
                    continue;
                }
                break;
            }
        }
    }
}

Instead of a Typed delegate such as Console.WriteLine. We can use an anonymous delegate as below:


Action<double> print = delegate(double data)
            {
                Console.WriteLine(data);
            };

Another way of writing an anonymous method is to use a lambda expression as below:


Action<double> print = (d) => Console.WriteLine(d);


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void ConsoleWrite(double data)
        {
            Console.WriteLine(data);
        }
        static void Main(string[] args)
        {
            //var buffer = new CircularBuffer<double>(capacity: 3);
            var buffer = new DataStructures.Buffer<double>();

            ProcessInput(buffer);

            //Action<double> print = delegate(double data)
            //{
            //    Console.WriteLine(data);
            //};
            //Action<double> print = (d) => Console.WriteLine(d);
            buffer.Dump(d=>Console.WriteLine(d));
            //buffer.Dump<double>(consoleOut);
            Console.WriteLine("---------");

            var resultInt =  buffer.AsEnumerableOf<double, int>();
            foreach (var item in resultInt)
            {
                Console.WriteLine(item);
            }

            ProcessBuffer(buffer);


            Console.ReadLine();
        }

        private static void ProcessBuffer(IBuffer<double> buffer)
        {
            var sum = 0.0;
            Console.WriteLine("Buffer: ");
            while (!buffer.IsEmpty)
            {
                sum += buffer.Read();
            }
            Console.WriteLine(sum);
        }

        private static void ProcessInput(IBuffer<double> buffer)
        {
            while (true)
            {
                var value = 0.0;
                var input = Console.ReadLine();

                if (double.TryParse(input, out value))
                {
                    buffer.Write(value);
                    continue;
                }
                break;
            }
        }
    }
}


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void ConsoleWrite(double data)
        {
            Console.WriteLine(data);
        }
        static void Main(string[] args)
        {
            //var buffer = new CircularBuffer<double>(capacity: 3);
            var buffer = new DataStructures.Buffer<double>();

            ProcessInput(buffer);

            //Action<double> print = delegate(double data)
            //{
            //    Console.WriteLine(data);
            //};
            Action<bool> print = (d) => Console.WriteLine(d);

            Func<double, double> square = d => d * d;
            Func<double, double, double> add = (x, y) => x + y;
            Predicate<double> isLessThanTen = d => d < 10;

            print(isLessThanTen(square(add(3, 5))));


            buffer.Dump(d=>Console.WriteLine(d));
            //buffer.Dump<double>(consoleOut);
            Console.WriteLine("---------");

            var resultInt =  buffer.AsEnumerableOf<double, int>();
            foreach (var item in resultInt)
            {
                Console.WriteLine(item);
            }

            ProcessBuffer(buffer);


            Console.ReadLine();
        }

        private static void ProcessBuffer(IBuffer<double> buffer)
        {
            var sum = 0.0;
            Console.WriteLine("Buffer: ");
            while (!buffer.IsEmpty)
            {
                sum += buffer.Read();
            }
            Console.WriteLine(sum);
        }

        private static void ProcessInput(IBuffer<double> buffer)
        {
            while (true)
            {
                var value = 0.0;
                var input = Console.ReadLine();

                if (double.TryParse(input, out value))
                {
                    buffer.Write(value);
                    continue;
                }
                break;
            }
        }
    }
}

There is one more Generic delegate not used daily but good to talk about. It is the Converter delegate that accepts to type arguments i.e. the "from" type and the "to" type. Let's see the code.


public static IEnumerable<TOutput> Map<T, TOutput>(this IBuffer<T> buffer, Converter<T, TOutput> converter)
        {
            return buffer.Select(x => converter(x));
        }


Converter<double, DateTime> converter = d => new DateTime(2015, 6, 13).AddDays(d);

var resultInDate = buffer.Map<double, DateTime>(converter);

foreach (var item in resultInDate)
{
    Console.WriteLine(item);
}

Or


//Converter<double, DateTime> converter = d => new DateTime(2015, 6, 13).AddDays(d);

            var resultInDate = buffer.Map<double, DateTime>(d => new DateTime(2015, 6, 13).AddDays(d));

Or considering the fact that lambdas are pretty smart.


//Converter<double, DateTime> converter = d => new DateTime(2015, 6, 13).AddDays(d);

            var resultInDate = buffer.Map(d => new DateTime(2015, 6, 13).AddDays(d));

The above code displays that the type arguments are inferred by buffer which is of type double which is an extension method if IBuffer<double>. and return type of the lambda which is a DateTime

Events and Generics

Let's say we want to raise an event when the circular buffer is full and somthing is being discarded. We want to raise the event showing the discarded value and the new item that is discarding that value.


public class CircularBuffer<T> : Buffer<T>
    {
        int _capacity;
        public CircularBuffer(int capacity = 10)
        {
            _capacity = capacity;
        }

        public override void Write(T value)
        {
            base.Write(value);
            if (_queue.Count > _capacity)
            {
                var discarded = _queue.Dequeue();
                var newItem = value;
                var args = new ItemDiscardedEventArgs<T>(discarded, newItem);
                ItemDiscarded(this, args);
            }
        }

        public event EventHandler<ItemDiscardedEventArgs<T>> ItemDiscarded;

        public bool IsFull { get { return _queue.Count == _capacity; } }
    }

The Program


using DataStructures;
using System;

namespace Client
{
    class Program
    {
        static void ConsoleWrite(double data)
        {
            Console.WriteLine(data);
        }
        static void Main(string[] args)
        {
            var buffer = new CircularBuffer<double>(capacity: 3);
            buffer.ItemDiscarded += buffer_ItemDiscarded;

            ProcessInput(buffer);


            ProcessBuffer(buffer);


            Console.ReadLine();
        }

        static void buffer_ItemDiscarded(object sender, ItemDiscardedEventArgs<double> e)
        {
            Console.WriteLine("Buffer Full. Discarded {0} New Item is {1}", e.Discarded, e.NewItem);
        }

        private static void ProcessBuffer(IBuffer<double> buffer)
        {
            var sum = 0.0;
            Console.WriteLine("Buffer: ");
            while (!buffer.IsEmpty)
            {
                sum += buffer.Read();
            }
            Console.WriteLine(sum);
        }

        private static void ProcessInput(IBuffer<double> buffer)
        {
            while (true)
            {
                var value = 0.0;
                var input = Console.ReadLine();

                if (double.TryParse(input, out value))
                {
                    buffer.Write(value);
                    continue;
                }
                break;
            }
        }
    }
}

Generic Constraints
Force a type parameter to have certain characteristics
  • Be a reference type or value type
  • Implement an interface
  • Derive from a base class
  • Have a default constructor
  • Be (or derive from) another generic type parameter

Let's begin with some models


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QueryIt
{
    public class Person
    {
        public string Name { get; set; }
    }

    public class Employee : Person
    {
        public int Id { get; set; }
        public virtual void DoWork()
        {
            Console.WriteLine("Doing real Work");
        }
    }

    public class Manager : Employee
    {
        public override void DoWork()
        {
            Console.WriteLine("Create a meeting");
        }
    }
}

And now let's add Entity Framework connect our model. After having Entity Framework, Let's have the below code:


using System;
using System.Data.Entity;
using System.Linq;

namespace QueryIt
{
    public class EmployeeDb : DbContext
    {
        public DbSet<Employee> Employees { get; set; }
        public DbSet<Manager> Managers { get; set; }
    }

    public interface IRepository<T> : IDisposable
    {
        void Add(T newEntity);
        void Delete(T entity);
        T FindById(int id);
        IQueryable<T> FindAll();
        int Commit();
    }

    public class SqlRepository<T> : IRepository<T>
    {
        private DbContext _ctx;
        private DbSet<T> _set;

        public SqlRepository(DbContext ctx)
        {
            _ctx = ctx;
            _set = _ctx.Set<T>();
        }

        public void Add(T newEntity)
        {
            _set.Add(newEntity);
        }

        public void Delete(T entity)
        {
            throw new NotImplementedException();
        }

        public T FindById(int id)
        {
            throw new NotImplementedException();
        }

        public IQueryable<T> FindAll()
        {
            throw new NotImplementedException();
        }

        public int Commit()
        {
            return _ctx.SaveChanges();
        }

        public void Dispose()
        {
            _ctx.Dispose();
        }
    }

}

And notice that the compiler is not happy when we build this code. It Says:

Before we fix this, let's jump in the DbSet definition once:

SqlRepository<T> is confident that i can accept any type as my type argument. But if it does accepts a primitive type, the DbSet will be unhappy because it can only accept a reference type. So Let's fix the code:


public class SqlRepository<T> : IRepository<T> where T : class
    {
        private DbContext _ctx;
        private DbSet<T> _set;

        public SqlRepository(DbContext ctx)
        {
            _ctx = ctx;
            _set = _ctx.Set<T>();
        }

        public void Add(T newEntity)
        {
            _set.Add(newEntity);
        }

        public void Delete(T entity)
        {
            throw new NotImplementedException();
        }

        public T FindById(int id)
        {
            throw new NotImplementedException();
        }

        public IQueryable<T> FindAll()
        {
            throw new NotImplementedException();
        }

        public int Commit()
        {
            return _ctx.SaveChanges();
        }

        public void Dispose()
        {
            _ctx.Dispose();
        }
    }


using System;
using System.Data.Entity;
using System.Linq;

namespace QueryIt
{
    class Program
    {
        static void Main(string[] args)
        {

            Database.SetInitializer(new DropCreateDatabaseAlways<EmployeeDb>());

            using (IRepository<Employee> employeeRepository = new SqlRepository<Employee>(new EmployeeDb()))
            {
                AddEmployees(employeeRepository);
                CountEmployees(employeeRepository);
            }


        }

        private static void CountEmployees(IRepository<Employee> employeeRepository)
        {
            Console.WriteLine(employeeRepository.FindAll().Count());
        }

        private static void AddEmployees(IRepository<Employee> employeeRepository)
        {
            employeeRepository.Add(new Employee { Name = "Scott" });
            employeeRepository.Add(new Employee { Name = "Chris" });
            employeeRepository.Commit();
        }
    }
}

Let's talk a little about Interface Constraint. Let's say our little Add method wants to have a validation before it adds something to the database. Like so:


public void Add(T newEntity)
        {
            if (newEntity.IsValid())
            {

            }
            _set.Add(newEntity);
        }

But this throws a compiler error that the type T does not implements IsValid. This is because every Type Parameter unless constrained derives from System.Object and currently only has methods that System.Object implements like ToString(), GetHashCode() etc.

In order to make something available to T lets have another Interface constraint like so:


public interface IEntity
    {
        bool IsValid();
    }

    public class Person
    {
        public string Name { get; set; }
    }

    public class Employee : Person //,IEntity
    {
        public int Id { get; set; }
        public virtual void DoWork()
        {
            Console.WriteLine("Doing real Work");
        }
    }

public class SqlRepository<T> : IRepository<T> where T : class, IEntity
    {
        private DbContext _ctx;
        private DbSet<T> _set;

        public SqlRepository(DbContext ctx)
        {
            _ctx = ctx;
            _set = _ctx.Set<T>();
        }

        public void Add(T newEntity)
        {
            if (newEntity.IsValid())
            {

            }
            _set.Add(newEntity);
        }

        public void Delete(T entity)
        {
            throw new NotImplementedException();
        }

        public T FindById(int id)
        {
            throw new NotImplementedException();
        }

        public IQueryable<T> FindAll()
        {
            return _set;
        }

        public int Commit()
        {
            return _ctx.SaveChanges();
        }

        public void Dispose()
        {
            _ctx.Dispose();
        }
    }

The compiler is happy the way we implemented our SqlRepository. It only throws a compiler error now where we instantiate a SqlRepository. The Type Argument for SqlRepository must implement IEntity.

So let's fix the Employee class now.


public class Employee : Person ,IEntity
    {
        public int Id { get; set; }
        public virtual void DoWork()
        {
            Console.WriteLine("Doing real Work");
        }

        public bool IsValid()
        {
            return true;
        }
    }

More about constraints
  • Either class or struct can be applied. And it should be written before any Interface constraint.
  • Either a concrete Type or class/struct can appear as a constraint. not both.
  • A constraint can come from a Type Parameter like so:
    
    public class SqlRepository<T, T2> : IRepository<T> where T : T2, IEntity
    
    This does not makes much sense in this context, but this is available.
  • In case you try to initialize T like so:
    
    public T FindById(int id)
    {
        T entity = new T();
    
        return T;
    }
    
    This will throw a compiler error because you are not sure if T could be instantiated by a new operator or not. So we apply another constraint here like so:
    
    public class SqlRepository<T> : IRepository<T> where T : class, IEntity, new()
    
    The new() constraint always comes at last. Now the compiler will always check if the type T has a default constructor.
  • In case you have another generic Type parameter T2 then you apply the constraint like so:
    
    public class SqlRepository<T, T2> : IRepository<T> where T : class, IEntity, new()
                                                       where T2 : class, IEntity //...
    
  • Its perfectly leagal to have constraints applied to one of the interfaces directly, but try to avoid this.

Here is the final code till now:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QueryIt
{

    public interface IEntity
    {
        bool IsValid();
    }

    public class Person
    {
        public string Name { get; set; }
    }

    public class Employee : Person ,IEntity
    {
        public int Id { get; set; }
        public virtual void DoWork()
        {
            Console.WriteLine("Doing real Work");
        }

        public bool IsValid()
        {
            return true;
        }
    }

    public class Manager : Employee
    {
        public override void DoWork()
        {
            Console.WriteLine("Create a meeting");
        }
    }
}


using System;
using System.Data.Entity;
using System.Linq;

namespace QueryIt
{
    public class EmployeeDb : DbContext
    {
        public DbSet<Employee> Employees { get; set; }
        public DbSet<Manager> Managers { get; set; }
    }

    public interface IRepository<T> : IDisposable
    {
        void Add(T newEntity);
        void Delete(T entity);
        T FindById(int id);
        IQueryable<T> FindAll();
        int Commit();
    }

    public class SqlRepository<T> : IRepository<T> where T : class, IEntity
    {
        private DbContext _ctx;
        private DbSet<T> _set;

        public SqlRepository(DbContext ctx)
        {
            _ctx = ctx;
            _set = _ctx.Set<T>();
        }

        public void Add(T newEntity)
        {
            if (newEntity.IsValid())
            {

            }
            _set.Add(newEntity);
        }

        public void Delete(T entity)
        {
            _set.Remove(entity);
        }

        public T FindById(int id)
        {
            return _set.Find(id);
        }

        public IQueryable<T> FindAll()
        {
            return _set;
        }

        public int Commit()
        {
            return _ctx.SaveChanges();
        }

        public void Dispose()
        {
            _ctx.Dispose();
        }
    }

}


using System;
using System.Data.Entity;
using System.Linq;

namespace QueryIt
{
    class Program
    {
        static void Main(string[] args)
        {

            Database.SetInitializer(new DropCreateDatabaseAlways<EmployeeDb>());

            using (IRepository<Employee> employeeRepository = new SqlRepository<Employee>(new EmployeeDb()))
            {
                AddEmployees(employeeRepository);
                CountEmployees(employeeRepository);
                QueryEmployees(employeeRepository);
            }

            Console.ReadLine();
        }

        private static void QueryEmployees(IRepository<Employee> employeeRepository)
        {
            var employee = employeeRepository.FindById(1);
            Console.WriteLine(employee.Name);
        }

        private static void CountEmployees(IRepository<Employee> employeeRepository)
        {
            Console.WriteLine(employeeRepository.FindAll().Count());
        }

        private static void AddEmployees(IRepository<Employee> employeeRepository)
        {
            employeeRepository.Add(new Employee { Name = "Scott" });
            employeeRepository.Add(new Employee { Name = "Chris" });
            employeeRepository.Commit();
        }
    }
}

Covariance
Let's add another method in our Program.cs

private static void DumpPeople(IRepository<Employee> employeeRepository)
{
    var employees = employeeRepository.FindAll();
    foreach (var employee in employees)
    {
        Console.WriteLine(employee.Name);
    }
}

In case we just want to dump the names, it should be in the spirit of good object oriented programming to convert this method to accept IRepository<Person>. And since Employee is a Person this piece of logic makes sense:


private static void DumpPeople(IRepository<Person> employeeRepository)
{
    var employees = employeeRepository.FindAll();
    foreach (var employee in employees)
    {
        Console.WriteLine(employee.Name);
    }
}

And use it as such:


using (IRepository<Employee> employeeRepository = new SqlRepository<Employee>(new EmployeeDb()))
{
    AddEmployees(employeeRepository);
    CountEmployees(employeeRepository);
    QueryEmployees(employeeRepository);
    DumpPeople(employeeRepository);
}

But, this throws a compiler error: And it seems odd from the perspective of Object Oriented Programming.

Notice this is not the case with IEnumerable<T>.


IEnumerable<Employee> temp = employeeRepository.FindAll();
IEnumerable<Person> temp = employeeRepository.FindAll();

The above code does not throws any compiler error. IQueryable<T> implements IEnumerable<T>. In the above code IQueryable<Employee> can be assigned to both IEnumerable<Employee> and IEnumerable<Person> If the above works fine, what is wrong with IRepository<T>

Well if we see the definition of IEnumerable<T> in the metadata:


using System.Collections;
using System.Runtime.CompilerServices;

namespace System.Collections.Generic
{
    public interface IEnumerable<out T> : IEnumerable
    {
        IEnumerator<T> GetEnumerator();
    }
}

Notice the out keyword with the Type parameter. out is a Generic Modifier. And it means that IEnumerable<T> is Covariant. This is in contrast to when the Generic Modifier is not present. That will be Invariant. As the name implies there is no variance.

The methods inside a covariant interface are allowed to return a type that is more derived than the type specified by the generic type parameter. Notice, more derived. In other words, a covariant interface like IEnumerable would allow GetEnumerator to return IEnumerable<Employee> even when the T is type Person. Employee is more derived than Person.

And that makes a lot of sense from an object-oriented point of view. If something is always giving me back Employee objects, why can't i just treat those objects as Person objects since every employee is a person. It's just that in c# that does require the out modifier to explicitly make the interface covariant, and covariance only works with delegates and interfaces.

Let's go back to our code and make IRepository<T> covariant.


public interface IRepository<out T> : IDisposable
{
    void Add(T newEntity);
    void Delete(T entity);
    T FindById(int id);
    IQueryable<T> FindAll();
    int Commit();
}

But now the compiler gives me some other errors:

The reason for these errors is that covariance is only supported when you have methods returning the covariant type parameter. It's dangerous and illeagal as far as the c# compiler is concerned to make an interface covariant when methods take parameters of type T. And if you think very deeply about this, it makes sense.

Imagine i have an IRepository<Employee> and i pass it to someone who treats it as an IRepository<Person>. It would be ok to read objects from that repository because all the objects it returns are Employees which derive from Person, so that's never going to cause a problem, but it would be bad to give that repository a Person as a parameter because the repository might try to interact with it or save it or use some characteristic of an Employee when the object it got was really only a Person.(Person object might not contain all the properties of Employee object.) So, can i make IRepository<T> covariant?

The answer would be no. But there are work arounds. Divide IRepository<T> to separate interfaces, one that contains read operations and the other that contains write operations.


 public interface IReadOnlyRepository<out T> : IDisposable
{
    T FindById(int id);
    IQueryable<T> FindAll();
}

public interface IRepository<T> : IReadOnlyRepository<T> ,IDisposable
{
    void Add(T newEntity);
    void Delete(T entity);
    int Commit();
}

And also change the signature for DumpPeople like so:


private static void DumpPeople(IReadOnlyRepository<Person> employeeRepository)
{
    var employees = employeeRepository.FindAll();
    foreach (var employee in employees)
    {
        Console.WriteLine(employee.Name);
    }
}

So this is a scenario of why covariance is requried. This can be generalized to I want to use some logic and work with base types as much as possible and covariance helps to enable that because i can treat IEnumerable<T> or IReadOnlyRepository<Employee> as an IEnumerable or IReadOnlyRepository<Person> and that allows me to share the logic in between Employee and Person

Contravariance
Contravariance is opposite of Covariance. Let's see a code where i want to add managers using an Employee Repository:

private static void AddManagers(IRepository<Employee> employeeRepository)
        {
            employeeRepository.Add(new Manager { Name = "Alex" });
            employeeRepository.Commit();
        }

This looks fine, but what if i want to convert my method to accept an IRepository<Manager> using an Employee Repository.


private static void AddManagers(IRepository<Manager> employeeRepository)
        {
            employeeRepository.Add(new Manager { Name = "Alex" });
            employeeRepository.Commit();
        }
This throws a compiler error because IRepository<T> is invariant.

This is where contravariance comes into picture. It uses the in generic modifier.


 public interface IRepository<in T> : IReadOnlyRepository<T> ,IDisposable
{
    void Add(T newEntity);
    void Delete(T entity);
    int Commit();
}
But there is still a compiler error :

To fix this let's create another interface which is contravariant:


public interface IWriteOnlyRepository<in T> : IDisposable
{
    void Add(T newEntity);
    void Delete(T entity);
    int Commit();
}

public interface IReadOnlyRepository<out T> : IDisposable
{
    T FindById(int id);
    IQueryable<T> FindAll();
}

public interface IRepository<T> : IReadOnlyRepository<T> , IWriteOnlyRepository<T>
{

}

And also fix the AddManager method:


private static void AddManagers(IWriteOnlyRepository<Manager> employeeRepository)
        {
            employeeRepository.Add(new Manager { Name = "Alex" });
            employeeRepository.Commit();
        }

Covariance : Employee casted to Person. This is possible because Employee will contain everything that a Person needs. Then you can read from this Person.

Contravariance : Employee will accept a Manager when writing to Employee because Manager contains everything that Employee needs.

Another Example of Covariance, Contravariance

Initial Code


void Main()
{
    IRepository<Animal> animalRepository = new Repository<Animal>();
    animalRepository.Add(new Animal{AnimalType="Four Legged"});

    IRepository<Animal> anotherAnimalRepository = animalRepository;

    IEnumerable<Animal> animals = anotherAnimalRepository.FindAll();

    foreach (var animal in animals)
    {
        Console.WriteLine (animal.AnimalType);
    }
}



public interface IRepository<T>
{
    void Add(T creature);
    IQueryable<T> FindAll();
}

public class Repository<T> : IRepository<T>
{
    private List<T> _beings;

    public Repository()
    {
        _beings = new List<T>();
    }

    public void Add(T being)
    {
        _beings.Add(being);
    }

    public IQueryable<T> FindAll()
    {
        return _beings.AsQueryable();
    }
}


public class Creature
{
    public String Type { get; set; }
}

public class Animal : Creature
{
    public String AnimalType {get; set;}
}

public class Dog : Animal
{
    public String Name {get; set;}
}

Covariance Code


void Main()
{
    IRepository<Animal> animalRepository = new Repository<Animal>();
    animalRepository.Add(new Animal{AnimalType="Four Legged", Type = "Animal"});

    //IRepository<Animal> anotherAnimalRepository = animalRepository;

    IReadOnlyRepository<Creature> creatureRepository = animalRepository;

    IEnumerable<Creature> creatures = creatureRepository.FindAll();

    foreach (var creature in creatures)
    {
        Console.WriteLine (creature.Type);
    }
}

public interface IReadOnlyRepository<out T>
{
    IQueryable<T> FindAll();
}


public interface IRepository<T> : IReadOnlyRepository<T>
{
    void Add(T creature);

}

public class Repository<T> : IRepository<T>
{
    private List<T> _beings;

    public Repository()
    {
        _beings = new List<T>();
    }

    public void Add(T being)
    {
        _beings.Add(being);
    }

    public IQueryable<T> FindAll()
    {
        return _beings.AsQueryable();
    }
}


public class Creature
{
    public String Type { get; set; }
}

public class Animal : Creature
{
    public String AnimalType {get; set;}
}

public class Dog : Animal
{
    public String Name {get; set;}
}

Contravariance Code


void Main()
{
    IRepository<Animal> animalRepository = new Repository<Animal>();
    animalRepository.Add(new Animal{AnimalType="Four Legged", Type = "Animal"});

    IWriteOnlyRepository<Dog> dogRepository = animalRepository;
    dogRepository.Add(new Dog{Name = "Bruno"});


    IEnumerable<Creature> creatures = animalRepository.FindAll();

    foreach (var creature in creatures)
    {
        Console.WriteLine (creature.Type);
    }

}


public interface IWriteOnlyRepository<in T>
{
    void Add(T creature);
}

public interface IReadOnlyRepository<out T>
{
    IQueryable<T> FindAll();
}


public interface IRepository<T> : IReadOnlyRepository<T>, IWriteOnlyRepository<T>
{


}

public class Repository<T> : IRepository<T>
{
    private List<T> _beings;

    public Repository()
    {
        _beings = new List<T>();
    }

    public void Add(T being)
    {
        _beings.Add(being);
    }

    public IQueryable<T> FindAll()
    {
        return _beings.AsQueryable();
    }
}


public class Creature
{
    public String Type { get; set; }
}

public class Animal : Creature
{
    public String AnimalType {get; set;}
}

public class Dog : Animal
{
    public String Name {get; set;}
}

More with Generics

Enums:

But this throws a compiler error, System.Enums are not allowed as a constraint

Math Problem

In case for the above code you need to need to implement it for double too, you might think this is something for which i can create a generic method. But there is no contstraint that says that the coming Type parameter must be able to do += or / operations. So for this case Generic method is not possible for now.

Generic Type inherits from Non Generic Type base class

Static field/property is no longer shared across for different type argument.

You need to pass the type parameter to get the number of instance with that type parameter. This prooves that a generic type with a specific type parameter is a different type as compared to that generic type with some other type parameter.

BUT, if you have the static field/property in a non generic base class, that is shared across different generic types with different type parameter.