When we use string data for an object type of
variable, an implicit conversion takes place and that is the variant behaviour
of a type. Variances can also be seen as a benefit for type-safe nature of .Net
languages.A type must be a sub-type for another type to
be a variant. When we use string data for an object type of
variable, an implicit conversion takes place and that is the variant behaviour
of a type. Variances can also be seen as a benefit for type-safe nature of .Net
languages.
Covariance:
The following is an example for the
covariant behaviour of a string array. String is a sub-type of an object type
and hence the code works fine with no error.
string[] a = new string[1];
object[] b = a;
Console.WriteLine(b[0]);
A is a single-element array of
System.String and B is an array of System.Object. The reading of a value from b
works better. On the otherhand, if we try to write an value into b,
b[0] = 120
it rises an
ArrayTypeMismatchException and message "Attempted
to store an element of the incorrect type into the array".
Even, the following code will work.
string a = "bala";
object b = a;
So, reference types are covariant, but
with some caveats. Covariance is applicable not only for assignments as stated
above, but also for return types from methods. It permits a method to have a
more derived return type than what is defined in the delegate.
Consider the following
example:
class Automobile
{
public Automobile CreateAuto() { return new
Automobile(); }
}
class Car : Automobile
{
public Car CreateCar()
{ Console.WriteLine("Car created");
return new Car();
}
}
class Application
{
public delegate Automobile CreateVehicle();
public static void GetVehicle(CreateVehicle
c)
{
c();
}
public static void Main()
{
CreateVehicle c2 = new CreateVehicle(new
Car().CreateCar);
GetVehicle(c2);
}
}
In the above program, Car is a sub-type of
Automobile and the delegate CreateVehicle is invoked, the car
object is created and returned. But the delegate is not declared as returning
the object of type Car, instead, Automobile.
Contra-variance:
Contravariance permits a method with
parameter types that are less derived than in the delegate type. This
means how delegates can be used with methods that have parameters of a type that
are base types of the delegate signature parameter type.
The following example shows how to use one event handler
in places where we would have had to use separate handlers. It creates an event
handler that accepts an EventArgs input parameter and use it with the
Button.MouseClick event that sends a MouseEventArgs type as a parameter, and
also with TextBox.KeyDown event that sends a KeyEventArgs
parameter.
System.DateTime lastActivity;
public Form1()
{
InitializeComponent();
lastActivity = new System.DateTime();
this.textBox1.KeyDown += this.MultiHandler; //works
with KeyEventArgs
this.button1.MouseClick += this.MultiHandler; //works
with MouseEventArgs
}
private void MultiHandler(object sender, System.EventArgs
e)
{
lastActivity = System.DateTime.Now;
}
Generic Covariance:
Strongly typed collections in System.Collections.Generic namespace
now allow variance and we can directly assign IEnumerable<int> to
IEnumerable<object> as shown in the code below.
List<int> ints = new List<int>();
ints.Add(1);
ints.Add(10);
ints.Add(42);
List<object> objects = new List<object>();
objects.AddRange(ints);
In .Net 4.0, the IEnumerable<T>
interface has become IEnumerable<out T>. The out indicates
that T has the output position in the interface, otherwise, the compiler emits
an error. The generic type T now supports covariance between the objects A and B
if A has a reference conversion to B.
The following code shows how a sequence
strings can be combined with a sequence of objects Using LINQ method.
IList<string> strings = new
List<string>(); IList<object> objects = new
List<object>();
var result = strings.Union(objects);
Also,
|