Thursday, June 5, 2014

Variance in C#



Variance in C#

Sub-classing and inheritance are two principle and main concepts of object oriented programming. Sub-classing refers to extending a class to enhance its behaviour or data so that it can either hold extra information or perform something more than its parent (base) class and hence is an important feature in programming languages that support inheritance. Variance is something that is related to inheritance and sub-classing. Wikipedia defines Variance as
Variance refers to how subtyping between more complex types (list of Cats versus list of Animals, function returning Cat versus function returning Animal, ...) relates to subtyping between their components. Depending on the variance of the type constructor, the subtyping relation may be either preserved, reversed, or ignored.
Now this is something that most of us would find hard to grasp even after working for a long time with various technologies and programming languages.
It would be great idea to understand this concept in simple terms which are easier to grab with some very simple examples and this is the whole idea behind this series of articles. C# supports two types of Variance.
1.       Covariance
2.       Contra-Variance.
Before we dig deep into these terms that sound like the terms used in Hollywood science fiction movies, let’s see some very simple code that can help us understand these concepts very easily and in an intuitive and practical manner.

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

namespace ConsoleApplication1
{
    public class animal
    {
        public string Name;
        public animal()
        {
            Name = string.Empty;
        }
        public animal(string name)
        {
            Name = name;
        }
    }
    public class dog : animal
    {
        public dog()
            : base()
        {

        }
        public dog(string name)
            : base(name)
        {
        }
    }
    public class cat : animal
    {
        public cat()
            : base()
        {

        }
        public cat(string name)
            : base(name)
        {
        }
    }
    class test
    {
        static void DoSomething(animal instance)
        {
        }

        static string Meow(cat instance)
        {
            return "Meow";
        }
        static string Bark(dog instance)
        {
            return "Bark";
        }
        static dog CreateNewDog(string name)
        {
            return new dog(name);//Valid
        }
        static dog CreateNewDogAnimal(string name)
        {
            return new animal(name);//Invalid -- Compile Time Error
        }

        static void main()
        {
            cat newCat = new cat();
            animal newAnimalCat = new cat();
            dog newDog = new dog();
            animal newAnimalDog = new dog();
            DoSomething(newCat); //Valid
            Meow(newCat); //Valid
            Meow(newAnimalCat);//Invalid -- Compile Time Error
            Bark(newDog);//valid
            Bark(newAnimalDog);//Invalid -- Compile Time Error

        }
    }
}

As you can see here we have three classes, animal is the base class and dog and cat classes derive from the animal class. The rules of Variance define where can you substitute a dog or cat in place of an animal and where can you not substitute an animal in place of a dog or cat.
Now let’s try to understand each of these sci-fi sounding terms individually. We will start with Covariance first, Covariance means that we can substitute a derived (sub) class in place of its base (parent) class. It means that at certain places we can pass an instance of dog or cat where an instance of animal is expected. As visible in the above code that you always pass an instance of dog or cat class in DoSomething method of test class and the compiler won’t complain about it and is perfectly type safe unless you have some other bug in your code. This behavior is known as Covariance.
Contra-Variance on the other hand means that you cannot substitute a base (parent) class where a derived (sub) class is expected. It means that you cannot pass a less specific type to a method that expects a more specific type. As you can see in the above code the method Meow expects an instance of cat (and so does the Bark method which expects an instance of dog class) but if you pass an instance of animal class to any of these methods compiler starts complaining even if the animal instance passed actually hold reference to an instance of cat (in case of Meow) or dog (in case of Bark) methods.
Covariance and Contra-Variance are actually very deep and advanced topics of computer science and especially in the field of object oriented programming, but in this series of articles we will try to explore them at our own pace with very simple examples.
To wind up our discussion today I would like to make a very simple and easy to remember statement:
“Input Parameters are Contra-Variant and Return Parameters are Co-variant in C#”.
Although this is a very simple statement but having said that, there remains a lot to be explored that lies between the simple and straight forward words of the above statement, which we will explore in subsequent articles of this series.

No comments:

Post a Comment