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