Deep understanding of C# covariance and inversion
msdn is explained as follows:
"Covariant" refers to the ability to use a type that is more derived than the originally specified derived type.
"Inversion" means that less derived types can be used.
The explanation is very correct. It's roughly like this, but it's not straightforward enough.
Straightforward understanding:
"Covariance" - > "harmonious change" - > "natural change" -> string - > Object: covariance.
"Inversion" - > "inverse constant change" - > "abnormal change" - > Object - > string inversion.
The above is my personal understanding of covariance and inversion. I think it's easier to remember words such as derivation, type, original assignment, larger and smaller.
Here is a joke:
Every day of the week should read as follows:
Monday = busy day;
Tuesday = death seeking day;
Wednesday = undead day;
Thursday = death day;
Friday = Fulai day;
Saturday = free and easy day;
Sun day = Sunday
To demonstrate covariance and inversion and the difference between them, please create a console program CAStudy and manually add two classes:
Because it is a demonstration, it is an empty class,
Just remember that Dog inherits from Animal,
Therefore, if Dog becomes Animal, it is a harmonious change (covariance), and if Animal becomes Dog, it is an abnormal change (inversion)
In the Main function, enter:
Because Dog inherits from Animal, Animal aAnimal = aDog; aDog will be implicitly transformed into Animal
However, List does not inherit List, so the following prompt appears:
If you want to convert, you should use the following code:
List<Animal> lstAnimal2 = lstDogs.Select(d => (Animal)d).ToList();
You can see how complex it is to turn an lstDogs into lstAnimal.
Because of this, Microsoft has added two keywords: out and in. Here is their msdn explanation:
Covariant is "covariant" in English and Contravariant is "Contravariant"
Why did Microsoft choose "Out" and "In" as features instead of them?
My personal understanding:
Because the English of covariance and inversion is too complex to reflect the difference between covariance and inversion, but out and in are very straightforward.
out: output (as a result), in: input (as a parameter)
Therefore, if a generic parameter is marked out, it means that it is used for output and can only be returned as a result. If a generic parameter is marked in, it means that it is used for input, that is, it can only be used as a parameter.
At present, the out and in keywords can only be used in interfaces and delegates. Microsoft's interfaces and delegates marked with out and in are roughly as follows:
First look at the first IEnumerable
As mentioned at the beginning, T is marked with out, so T represents output, that is, it can only be returned as a result.
public static void Main() { Dog aDog = new Dog(); Animal aAnimal = aDog; List<Dog> lstDogs = new List<Dog>(); //List<Animal> lstAnimal = lstDogs; List<Animal> lstAnimal2 = lstDogs.Select(d => (Animal)d).ToList(); IEnumerable<Dog> someDogs = new List<Dog>(); IEnumerable<Animal> someAnimals = someDogs; }
Because T can only return results, T will not be modified. The compiler can infer that the following statement is legal, so
IEnumerable<Animal> someAnimals = someDogs;
You can check the compiler and decompile the code as follows:
Although I passed the C# comp il er check, I didn't know about covariance and inversion, so I had to cast obediently.
Here I see this sentence:
IEnumerable<Animal> enumerable2 = (IEnumerable<Animal>) enumerable1;
So can List lstAnimal3 = (List)lstDogs; And?
To answer this question, you need to look back at the chapter of Clr via C# on generics and interfaces. I won't explain it,
The answer is No.
The above demonstration is covariance, and the next demonstration is inversion.
In order to demonstrate inversion, you need to find an in marked interface or delegate. The simplest is:
Add to the Main function:
Action<Animal> actionAnimal = new Action<Animal>(a => {/*Let the animals bark*/ }); Action<Dog> actionDog = actionAnimal; actionDog(aDog);
Obviously, actionAnimal makes animals call, because Dog is Animal, so since all animals can call, Dog must also call.
In keyword: inverse, representing input, representing that it can only be used and cannot be used as a return value, so the C# compiler can infer that this generic type can only be used according to the in keyword, so Action actionDog = actionAnimal; It can be checked by the compiler.
Demonstrate the Out keyword again:
Add two classes:
public interface IMyList<out T> { T GetElement(); }
public class MyList<T> : IMyList<T> { public T GetElement() { return default(T); } }
Because of the out keyword, the following code can be compiled
IMyList<Dog> myDogs = new MyList<Dog>(); IMyList<Animal> myAnimals = myDogs;
Modify the above two classes to:
public interface IMyList<out T> { T GetElement(); void ChangeT(T t); }
public class MyList<T> : IMyList<T> { public T GetElement() { return default(T); }
public void ChangeT(T t) { //Change T } }
compile:
Because T is modified by out, T can only be used as a parameter.
Similarly, modify the two classes as follows:
public interface IMyList<in T> { T GetElement(); void ChangeT(T t); }
public class MyList<T> : IMyList<T> { public T GetElement() { return default(T); } public void ChangeT(T t) { //Change T } }
Use the in keyword this time.
compile:
Because it is marked with the in keyword, T can only be used and cannot be used as a return value.
The last modified code is:
public interface IMyList<in T> { void ChangeT(T t); } public class MyList<T> : IMyList<T> { public void ChangeT(T t) { //Change T } }
The compilation was successful because in represents the inversion, so
IMyList<Animal> myAnimals = new MyList<Animal>(); IMyList<Dog> myDogs = myAnimals;
Can compile successfully!.