神刀安全网

Difference between Covariance and Contravariance in C# Generics

In this article, we will discuss:

  • Covariance in Generics
  • Contravariance in Generics
  • The difference between Covariance and Contravariance in C#
  • Summary

Covariance in Generics:

Every variable has a type assigned to it,and you can assign an object of a more derived type to a variable of one of its base types. This is calledassignment compatibility.

 class Animal     {         public int NumberOfLegs = 4;     }     class Dog : Animal     {     }     class Program     {         static void Main()         {             Animala1 = new Animal();             Animala2 = new Dog();             Console.WriteLine("Number of dog legs: {0}", a2.NumberOfLegs);         }     }   

Output:

Number of dog legs: 4

The above example explains assignment compatibility with a base class Animal and a class Dog derived from Animal. In Main, you can see that the code creates an object of type Dog and assigns it to variable a2 of type Animal.

Assignment compatibility means that you can assign a reference of a more derived type to a variable of a less derived type.

Let’s look at another example to understand it better.

  class Animal    {        public int Legs = 4;    } // Base class class Dog : Animal { }// Derived class   delegate T Factory<T>( );//delegate Factory   class Program {     static DogMakeDog()//Method that matches delegate Factory     {         return new Dog();     }       static void Main()     {         Factory<Dog> dogMaker = MakeDog;//Create delegate object.         Factory<Animal> animalMaker = dogMaker;  //Attempt to assign delegate object.         Console.WriteLine(animalMaker().Legs.ToString());     } } 

  • This code adds a generic delegate named Factory, which takes a single type parameter T, takes no method parameters, and returns an object of type T.
  • We have added a method named MakeDog that takes no parameters and returns a Dog object. This method, therefore, matches delegate Factory if we use Dog as the type parameter.
  • The first line of Main creates a delegate object whose type is delegate Factory and assigns its reference to variable dogMaker, of the same type.
  • The second line attempts to assign a delegate of type delegate Factory to a delegate type variable named animalMaker of type delegate Factory<Animal>.

But when you will run the above code, you will get a compilation error.

Cannot implicitly convert type ‘Factory<ConsoleApplication16.Dog>’ to ‘Factory<ConsoleApplication16.Animal>’

The problem is that although Dog derives from Animal, delegate Factory<Dog> does not derive from delegate Factory<Animal>So Assignment compatibility doesn’t apply because the two delegates are unrelated by inheritance.

In this situation, you would be able to use a constructed delegate type created with a derived class, and it would work fine,since the invoking code would always be expecting a reference to the base class.

This constant relation between the use of a derived type only as an output value and the validity of the constructed delegate is called covariance.

To let the compiler know that this is what you intend, you must mark the type parameter in the delegate declaration with the out keyword.

 class Animal    {        public int Legs = 4;    } // Base class class Dog : Animal { }// Derived class   delegate T Factory<out T>( );//delegate Factory, Keyword specifying covariance of the type parameter   class Program {     static DogMakeDog()//Method that matches delegate Factory     {         return new Dog();     }       static void Main()     {         Factory<Dog> dogMaker = MakeDog;//Create delegate object.         Factory<Animal> animalMaker = dogMaker;  //Attempt to assign delegate object.         Console.WriteLine(animalMaker().Legs.ToString());     } } 

Output:

4

Contravariance in Generics:

class Animal {      public int NumberOfLegs = 4;  } class Dog : Animal { } class Program {     delegatevoid Action1<in T>(T a);//in is the Keyword for contravariance     static void ActOnAnimal(Animal a)     {         Console.WriteLine(a.NumberOfLegs);     }     static void Main()     {         Action1<Animal> act1 = ActOnAnimal;         Action1<Dog> dog1 = act1;         dog1(new Dog());     } } 

Output:

4

  • The above code declares a delegate named Action1 that takes a single type parameter and a single method parameter whose type is that of the type parameter, and it returns no value.
  • The code also contains a method called ActOnAnimal, whose signature and void return type match the delegate declaration.
  • The first line in Main creates a constructed delegate using type Animal and method ActOnAnimal, whose signature and void return type match the delegate declaration. In the second line, however, the code attempts to assign the reference to this delegate to a stack variable named dog1, of type delegate Action1<Dog>.

By default, you can’t assign the two incompatible types. But in some scenarios it would work fine.

This is true whenever the type parameter is used only as an input parameter to the method in the delegate because even though the invoking code passes in a reference to a more derived class, the method in the delegate is only expecting a reference to a less derived class.This relation, allowing a more derived object where a less derived object is expected, is called contravariance. To use it, you must use the in keyword with the type parameter

Difference between Covariance & Contravariance:

Difference between Covariance and Contravariance in C# Generics

Difference between Covariance and Contravariance in C# Generics

Summary:

In this article, we have discussed:

  • Covariance in Generics
  • Contravariance in Generics
  • The difference between Covariance and Contravariance in C#

Thanks for visiting !!

© 2016,admin. All rights reserved.

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Difference between Covariance and Contravariance in C# Generics

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址