Based on the in depth analysis of covariant and contravariant in.Net
- 2020-06-07 04:21:52
- OfStack
Covariant and contravariant should start with object - oriented inheritance. Inheritance relationship refers to the relationship between a subclass and its parent; A subclass inherits from a superclass and an instance of a subclass is an instance of the superclass. For example, Animal is the parent class, Dog is a subclass from Animal; If an object is of type Dog, it must be Animal.
Covariant inversion USES inheritance to transform delegates or generic interfaces of different parameter types or return value types. I admit it's a convoluted statement, but if you're feeling convoluted, read on.
If one method accepts the Dog parameter, then the other method that accepts the Animal parameter must also accept the parameter of this method. This is the inverse transition from Animal to Dog. If a method requires a return value of Animal, then the method that returns Dog must satisfy its return value. This is the transition from Dog to Animal is covariant.
The transition from a subclass to a superclass is covariant covariant used for return value types using the out keyword
The shift from the superclass to the subclass direction is contravariant and used to method the parameter types using the in keyword
The coinverse in covariant contravariant is relative to the direction of inheritance chain.
1. Array covariant:
Animal[] animalArray = new Dog[]{};
The above 1 line is legal. The declared array data type is Animal, but the actual value is assigned to an Dog array. Every Dog object can be safely converted to Animal. The transition from Dog to Animal is up the inheritance chain and therefore covariant
2. Covariant and contravariant in a delegate
1. Covariance in the delegate
// The return value of the delegate definition is Animal The type is the parent class
public delegate Animal GetAnimal();
// The return value in the delegate method implementation is Dog , it is a subclass
static Dog GetDog(){return new Dog();}
//GetDog The return value of is Dog, Dog is Animal The subclass. return 1 a Dog It must be the same as going back 1 a Animal ; So the assignment to the delegate below is valid
GetAnimal getMethod = GetDog;
2. Contravariant in the delegate
// The definition parameter type in the delegate is Dog
public delegate void FeedDog(Dog target);
// The parameter type in the actual method is Animal
static void FeedAnimal(Animal target){}
// FeedAnimal is FeedDog A valid way to delegate, because the type of argument the delegate accepts is Dog ; while FeedAnimal The accepted parameter is animal . Dog It can be implicitly converted to Animal , so the delegate can safely do the type conversion, the correct execution of the delegate method;
FeedDog feedDogMethod = FeedAnimal;
The argument when you define the delegate is the subclass, and in fact the argument of the delegate method is the broader superclass Animal, which is the shift from the superclass to the subclass, which is the contravariant
3. Covariant and contravariant of generic delegation:
1. Contravariant in a generic delegate
The following statement of authorization:
public delegate void Feed<in T>(T target);
The Feed delegate accepts 1 generic type T. Note that there is 1 in keyword in the Angle bracket of the generic type. The purpose of this keyword is to tell the compiler that the type T may invert when assigning the delegate
// First statement 1 a T for Animal The commission
Feed<Animal> feedAnimalMethod = a=>Console.WriteLine( " Feed animal lambda " );
// will T for Animal Delegate assignment to T for Dog This is legal because when you define a generic delegate in Keywords if put in Remove the keyword and the compiler will consider it illegal
Feed<Dog> feedDogMethod = feedAnimalMethod;
2. Covariance in generic delegates
The following statement of authorization:
public delegate T Find<out T>();
The Find delegate returns an instance of the generic type T with an out keyword in the Angle bracket indicating that the T type is likely to be covariant
// The statement Find<Dog> entrust
Find<Dog> findDog = ()=>new Dog();
// The statement Find<Animal> Delegate and will findDog Assigned to findAnimal It's legal. Type T from Dog to Animal Transitions are covariant
Find<Animal> findAnimal = findDog;
4. Covariant and contravariant in generic interfaces:
Covariant inversion in a generic interface is very similar to that in a generic delegate, except that the Angle brackets of the generic definition are replaced with the interface definition.
1. Contravariant in generic interfaces
The following interface definitions:
public interface IFeedable<in T>
{
void Feed(T t);
}
The generic T of the interface is preceded by an in keyword to indicate that the generic interface may be contravariant
The generic type FeedImp is shown below < T > , to implement the generic interface above; It should be noted that the covariant and contravariant keyword in, out is not used in generic classes, the compiler does not allow
public class FeedImp<T>:IFeedable<T>
{
public void Feed(T t){
Console.WriteLine( " Feed Animal " );
}
}
Consider an example of using an interface inverter:
IFeedable<Dog> feedDog = new FeedImp<Animal>();
The code above will FeedImp < Animal > The type is assigned to IFeedable < Dog > The variables; Animal is converted to Dog, so it's contravariant
2. Covariant in generic interfaces
Definition of the following interface:
// The return value of the delegate definition is Animal The type is the parent class
public delegate Animal GetAnimal();
// The return value in the delegate method implementation is Dog , it is a subclass
static Dog GetDog(){return new Dog();}
//GetDog The return value of is Dog, Dog is Animal The subclass. return 1 a Dog It must be the same as going back 1 a Animal ; So the assignment to the delegate below is valid
GetAnimal getMethod = GetDog;
0
The T generic interface USES the out keyword to indicate that the interface may be covariant. The following generic interface implementation class
public class Finder<T>:IFinder<T> where T:new()
{
public T Find(){
return new T();
}
}
// The use of covariance ,IFinder The generic type of is Animal But because there is out Keyword, I can be Finder<Dog> Assign a value to it
IFinder<Animal> finder = new Finder<Dog>();
The concepts of covariant and contravariant are not easy to understand and can be understood by thinking in real code. Is this stuff going to work? The answer is yes, better code reuse is possible through covariant and contravariant. Reuse is an eternal pursuit of software development.