The introduction of variants is to increase the flexibility of generics by making it possible for a variable of a generic type to be converted to a type for compatibility when assigned. Let's look at an example:
public delegate void Dowork<t> (T arg);
........
Dowork<a> del1=delegate (A arg) {//...};
Dowork<b> Del2=del1;
b bb=new B ();
Del2 (BB);
Where A, B is two classes, Class B inherits Class A, that is: public class a{...} public class b:a{...}
The above code could not compile successfully because the type could not complete the conversion. To solve this problem, a variant is introduced.
(1) Covariance and contravariance
Variants can be divided into covariance and contravariance, whether covariant or contravariant, from the name can be seen in the difference because it is in the direction of conversion.
For covariance and Contravariance, MSDN explains the following:
Covariant refers to the ability to use a more derived type than the original specified derived type.
"Contravariance" refers to the ability to use less derived types.
Of course, this has been explained very clearly, the following on my personal view to explain.
Let us look at the example above, if we make the following changes:
public delegate void Dowork<in t> (T Arg);
........
Dowork<a> del1=delegate (A arg) {//...};
Dowork<b> Del2=del1;
b bb=new B ();
Del2 (BB);
After the code is modified, the above code can be compiled. In fact, this example belongs to the inverse, the example of the conversion direction of the generic delegate variable and the type of implicit conversion direction in the form of "inverse", the opposite direction, it is called "inversion."
Of course, covariant appetite inversion is relative, covariant in the form of a generic interface or generic delegate variable in the direction of assignment and the implicit conversion direction of the type. Let's take a look at the covariance example:
Defining types
public class a{}
public class b:a{}
Generic interface
public interface Itest<out t>
{
T Create ();
}
Implementing interfaces
public class test<y>:itest<y> where Y:new ()
{
Public Y Create ()
{
return new Y ();
}
The following code-assignment conversions
Itest<b> t1=new test<b> ();
Itest<a> t2=t1;//Assignment succeeded
A a=t2.create ();
Console.WriteLine (A.gettype (). Name);
}
In the example above, Itest<b>->itest<a>, however, since type B is derived from type A, it can be implicitly converted to a variable of type A, so the direction of assignment of the generic variable is consistent with the implicit conversion direction of the type specified by the type parameter, which is the " Co-change ".
Of course, variants support only generic interfaces and delegates, and variants cannot be used for classes and structs.
(2) input and output of type parameters
Through the introduction of covariance and contravariance, it is known that generic definitions can use variants by adding an in or out modifier for the type parameter. Types that are decorated with the in modifier can be interpreted as input types, which can only be used as input parameters, and an out modifier to represent the type as an output type, typically used for the return value of a method.
Let's look at an example that uses out:
1 usingSystem;2 usingSystem.Collections.Generic;3 usingSystem.Linq;4 usingSystem.Text;5 usingSystem.Threading.Tasks;6 7 namespaceMy8 {9 #regionDefining classesTen Public classBase {} One Public classtest:base {} A #endregion - - #regionDefining delegates the Public DelegateT mydelegate< outT>(); - #endregion - - class Program + { - Static voidMain (string[] args) + { Amydelegate<test> D1 =Delegate() at { - return NewTest (); -};//using anonymous Methods - - //then define a mydelegate delegate, type base -Mydelegate<base> D2 =D1; in //Test Call delegate variable D2 -Base B =D2 (); to //True type of output variable B +Console.Write ("type of variable B: {0}", B.gettype (). Name); - the Console.read (); * } $ }Panax Notoginseng}
In the example, the implicit conversion of a type is consistent with the assignment of a delegate variable, so the variant of this instance is covariant.
The output type parameter is given above, then we look at the input type parameter, the code is as follows:
1 usingSystem;2 usingSystem.Collections.Generic;3 usingSystem.Linq;4 usingSystem.Text;5 usingSystem.Threading.Tasks;6 7 namespaceMy8 {9 #regionDefining typesTen Public Abstract classWorkbase One { A Public Abstract voidRun (); - } - Public classFinancemng:workbase the { - Public Override voidRun () - { -Console.WriteLine ("Financial Management"); + } - } + Public classPaymentmng:workbase A { at Public Override voidRun () - { -Console.WriteLine ("Compensation Management"); - } - } - Public classJobanalymng:workbase in { - Public Override voidRun () to { +Console.WriteLine ("Job Analysis"); - } the } * #endregion $ Panax Notoginseng #regionGeneric interface - Public Interfaceiwork<inchW> the { + voidDoWork (w w); A } the + //implementing a generic interface - Public classMywork<t>: iwork<t>whereT:workbase $ { $ Public voidDoWork (T W) - { - W.run (); the } - }Wuyi #endregion the - class Program Wu { - Static voidMain (string[] args) About { $Iwork<workbase> everywork =NewMywork<workbase>(); - - //Call a -Iwork<financemng> Work1 =everywork; AWork1. DoWork (Newfinancemng ()); + the //Call Two -Iwork<paymentmng> WORK2 =everywork; $Work2. DoWork (Newpaymentmng ()); the the //call three theIwork<jobanalymng> Work3 =everywork; theWork3. DoWork (Newjobanalymng ()); - in Console.read (); the } the } About}
In this case, the direction of the assignment is reversed in the direction of the implicit conversion of the type.
From the previous example, you can draw a rule that the input type parameter (using the in modifier) is contravariant, and the output type parameter (using the Out modifier) is covariant ....
Variants (covariance and contravariance)