The covariance and inversion of the generic parameters are clarified

Source: Internet
Author: User

Covariance and contravariance (covariant and contravariant), a lot of people are confused, I have been confused. In fact, the concept of covariance and inversion is confused, even completely do not know, the general programmer also has no significant impact. However, if you want to improve your level and want to understand some of the generic interfaces and generic classes in the. Net Framework class library, you might want to figure out what LINQ is about.

Then again, to find out, in fact, it is quite laborious.

If you are confused with these two concepts, believe me, read the following text carefully, you will have a clear understanding of the covariance and contravariance of the generic parameters.

In order to master the concept of covariance and inversion, it is necessary to understand and master the concepts of interfaces, delegates and generics first, otherwise it is not easy to understand covariant and contravariant. The mastery of these basic knowledge is not the responsibility of this essay, please read your own books.

First clarify a few knowledge points

(1) in the. Net Framework 4.0, the concept of covariance and contravariance, when applied to a generic variable type parameter, can only be used for generic interfaces and generic delegates (refer to https://msdn.microsoft.com/EN-US/library/ dd799517 (V=vs.110,d=hv.2). aspx is clear in this. To understand this, we can delineate the scope of our thinking and make understanding and learning simpler and clearer. Of course, in addition to the generic type parameters, the concept of covariance and contravariance is also used elsewhere.

(2) It is not safe to convert a subclass to a parent class at any time, while the parent class converts to the subclass. It is not safe to convert a parent class to a subclass because the subclass contains all of the information of the parent class and is converted to the parent class, and the parent class does not contain the information that is unique to the child class. Of course, forcing a type conversion is possible if a variable in the parent class is actually an object of the subclass, which is a different matter.

(3) Take a look at MSDN about covariance, contravariance, and unchanging (you can skip English and see my translation.) ):

Covariance and contravariance are terms, refer to the ability, use a, less derived, less specific, or more derived Ty PE (more specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using Generi C types. When you were referring to a type system, covariance, contravariance, and invariance had the following definitions. The examples assume a base class named base and a derived class named derived.

  • Covariance

    Enables a more derived type than originally specified.

    You can assign an instance of ienumerable<derived> (IEnumerable (of Derived) in Visual Basic) to a variable O F type ienumerable<base>.

  • Contravariance

    Enables a more generic (less derived) type than originally specified.

    You can assign an instance of ienumerable<base> (IEnumerable (of Base) in Visual Basic) to a variable of type ienumerable<derived>.

  • Invariance

    Means that's can use only the type originally specified; So a invariant generic type parameter is neither covariant nor contravariant.

    You cannot assign an instance of ienumerable<base> (IEnumerable (of Base) in Visual Basic) to a variable of T Ype ienumerable<derived> or vice versa.

Simply double-click:

The term covariance and contravariance refers to the ability to override the original specified type with a type that is more generalized (more toward the parent type) or more specific (more toward the subtype). Generic parameters support covariance and contravariance, which gives greater flexibility for the assignment of generic types and their use. When we are talking about a type system (covariant and contravariant concepts not only apply to generic type systems), covariance, contravariance, and invariant are defined as follows. In the following example, base is the class name of the parent class, and derived is the class name of the subclass.

    • Co-change

Covariance allows you to replace the original type with a more specific type. For example, you can assign a ienumerable<derived> instance to a variable of type ienumerable<base> .

I add: substituting a more specific type for the original type, that is to say, the subtype can be converted to the parent type. It is called covariance, considering that a subtype object is converted to a parent type object.

    • Inverse change

Contravariance allows you to replace the original type with a more generalized type. For example, you can assign a ienumerable<base> instance to a variable of type ienumerable<derived>.

    I add: substituting a more generalized type for the original type, that is to say, the parent type is converted to a subtype. This is the opposite of the covariance above, called contravariance.

    • Not change

Invariant means that you can only use the original specified type; therefore, a "constant" generic type parameter is neither covariant nor contravariant. You cannot assign an instance of a ienumerable<derived> type to a variable of type ienumerable<base>. Not in turn.

(4) covariant, which is represented in C # using the Out keyword, and contravariant using the In keyword.

Please note that this article is limited to the covariance and contravariance of generic type parameters, which is the most common use of these two concepts. To figure this out, we basically fix the covariance and contravariance.

Okay, let's uncover the mystery of covariance and contravariance.

As we said earlier, the covariance and contravariance of generic type parameters are used only for generic delegates and generic interfaces. So, it's easy to enumerate the exhaustive, not too complicated, as long as we make the covariance and contravariance in the generic delegate and the generic interface clear, it is equal to the covariance and contravariance clear.

Let's look at the Commission first.

Covariance and contravariance in generic delegates

Delegates are similar to function pointers in C + +. A delegate is a reference type. Therefore, you can declare a variable of a delegate type, or you can assign a value to the variable. A variable of a delegate type holds a method, including the parameter and return value of the method.

Look at the code first:

1      Public class Program2     {3         DelegateTResult fn<inchT outTresult>(T arg);4         Static voidMain ()5         {6Fn<object, argumentexception> fn1 = obj = obj = =NULL?NewArgumentException ():NULL;7fn<string, Exception> fn2;8FN2 = fn1;9 Exception e= FN2 ("123");Ten         } One}

Line 3rd, first define a delegate type Fn, the delegate type has two generic parameters, one is T, with in modifier, is the inverse variable, the other is TResult, with out decoration, is a covariant variable.

Line 6th, declaring an Fn1 object (variable) of type FN, the two generic parameters of the delegate object are of type objects and ArgumentException, respectively. FN1 is implemented using a lambda expression, which verifies the input parameter obj of type object, and returns a ArgumentException exception if obj is NULL, otherwise returns NULL. The lambda expression is equivalent to the following methods:

1ArgumentException function (Objectobj)2 {3     if(obj = =NULL)4 {5       returnArgumentException;6 }7     Else8 {9       return NULL;Ten } One }

Line 7th, and then declare an Fn2 object of type FN, the two generic parameters of the delegate object are of type string and exception, respectively.

In line 8th, we assign the FN1 directly to fn2, in other words, implicitly assigning fn<object, argumentexception> type variables to fn<string, exception>.

In line 9th, we call the Fn2 method with a string "123", which returns a null indicating that the "123" object is a legitimate parameter object and does not need to throw an exception.

Let's analyze the covariance and contravariance in this.

First, it is important to keep in mind that the so-called covariance and contravariance are referred to as "types" rather than objects . As mentioned earlier, an object of type can only be converted from a subclass to a parent class, an actual parent class object that can never be safely converted to a subclass. So, don't expect to say "contravariance" can realize the dream of the parent class object rotor class object.

Fn2=fn1 This code of covariance and Contravariance, is the type of covariance and contravariance, rather than the type of the object. Fn2 and fn1 are TResult fn< in T, an instance object of this delegate type ofout tresult> (t arg) . Assigning fn1 to Fn2 is a conversion from fn1 to FN2.

After the generics are removed, the methods of FN1 and FN2 are as follows:

Fn1:argumentexception fn1 (object); Fn2:exception fn2 (string);

From the definition of the generic delegate FN, we can tell that the return value is covariant and the parameter value is contravariant. This means that an object instance of FN can be converted to an object instance of another FN by the condition that the type of the returned value of the object instance being converted must be more specific, and the type of the parameter value must be more generalized. In the case of FN2 and Fn1, the ArgumentException type is more specific than the exception type , and the object type is more generalized than the string type .

Why does the above rule exist? Further, why are these rules a reasonable existence? Unreasonable rules, even if they exist, cannot be executed.

Let's take a look at the essence of the assignment statement fn2=fn1. In fact, the quality can be seen from the above exception e = fn2 ("123") statement.

Its essence is:

(1) When the FN1 is assigned to FN2, if the Fn2 method is called, then the parameters passed to it and its return value must be in accordance with the definition of fn2, that is, when called, the parameter is a string, and the required return value is exception;
(2) Because FN1 is assigned to FN2, when FN2 is called, the actual execution is fn1. Therefore, the method that is actually executed requires that the parameter being received is object, and the return value is ArgumentException.

In two points, the executed body requires object, and the passed string, no problem; The executed body returns a return value of type ArgumentException, and fn2 requires a value of type exception, no problem. Therefore, the above rules on covariance and contravariance are reasonable and feasible.

See clearly, whether it is a type of contravariant or type covariance, it is required that an instance object (not a type parameter) be converted from a subclass to a parent class. If this is the reverse, it must be compiled without passing.

There are two lines of gossip, hoping that it will not affect our understanding of the concept of covariance and contravariance.

(1) In the example above, if the in and out keywords in the delegate FN definition are removed, the FN2=FN1 statement cannot be compiled. Why is it? We have not already analyzed above, fn1 to FN2 conversion, is a logical thing, reasonable and legitimate, why do compile errors? My understanding is that this is the so-called "immutable" rule that works, and MSDN has made it clear that if a type variable in a generic delegate is neither covariant nor contravariant, then it is immutable, and since it is immutable, the conversion is forbidden by the compiler. Conversely, if you want to make them mutable, I'm sorry, please explicitly use the In or (and) out keyword to explicitly tell the compiler what you think.

(2) In and out keywords do not have a negative effect on generic delegates, only the delegate that you define can be used more broadly. Therefore, the CLR via C # Book explicitly recommends that you use the in and out keywords to decorate the delegate's generic parameters whenever possible. The original text is as follows: When using delegates-take generic arguments and return values, it's recommended to always specify the in and Out keywords for contravariance and covariance whenever possible, as doing this have no ill effects and enables your Delega Te to is used in more scenarios.

(3) The concept of covariance and contravariance applies not only to generic type parameters, as shown in the following example.

1     DelegateObject mycallback (Circle Cir);2 3     stringSomeMethod (Shape s)4     {5         returns.area.tostring ();6     }7 8     voidTESTFN ()9     {TenMycallback CALLBACKFN =SomeMethod; One}

In the example, shape is the base class, and Circle is its derived class.
Line 1th defines a delegate mycallback, and line 3rd through 6th defines a method SomeMethod whose signature is inconsistent with the mycallback requirements. However, from line 10th, we can assign SomeMethod to a delegate variable of type Mycallback. Here, also called Writers Association and Contravariance.

It is concluded that the concept of covariance and contravariance is not only applied to generic type parameters. This pair of concepts also appears in other places. However, covariance and contravariance in generic type parameters are the most important application places for this concept.

Covariance and Contravariance in generic interface applications

If you understand the covariance and contravariance associated with generic delegates above, then covariance and contravariance in generic interface applications are relatively easy to understand.

What exactly is an interface?

Limited to my level and space, there is no system to describe the interface. Just write some of the points that I think are important and related to covariance and contravariance here.

An interface is a measure taken by the. Net framework to circumvent the complexities of multiple inheritance and to enjoy the benefits of multiple inheritance. In the interface, only some methods (their return value type, type and number of parameters) are specified. Interfaces and abstract base classes have similar places, but there are many differences. An interface is also a type, you can declare variables of an interface type, or you can assign values to those variables. An instance object that implements an actual class of an interface, which can be assigned to an interface variable, implicitly. This is like saying that the interface is the base class for the class that implements the interface, assigning a subclass object to the base class object, which of course does not need to display the transform.

When we talk about covariance and contravariance, we can say that interfaces, in fact, are a collection of methods.

As shown in the following code:

1 //Define an interface first    2 InterfaceITest3 {4     stringSomeMethod (ObjectArg);5 } 6 //Redefine a class that implements an interface 
7 classclasstest:itest8 {9 Public stringSomeMethod (ObjectArg)Ten { One returnArg. ToString (); A }
public void Othermethod () {...}
- }

With the above definition, the following variable declarations can be made:
ITest objinterface = new Classtest ();

Because Classtest inherits the ITest interface, the new Classtest object can be implicitly assigned to a variable of type itest.

When we declare a variable of an interface type, this variable can invoke the method specified by the interface, but cannot call the class that the actual object represented by this interface variable belongs to, other methods that do not belong to the above interface. In the above example, Objinterface can call SomeMethod, but cannot call Othermethod.

Besides, the generic interface

The generic interface is the generic interface,:).

Generic interfaces can define generic type parameters, such as:itestinterface<t>.

Similar to generic delegates, you can also qualify covariance and contravariance for the type parameters of an interface. For example. A covariant generic interface in the NET Framework class library:

Public interface ienumerable< outt>: IEnumerable

This generic interface has a type parameter T, which is modified with out, stating that type T is covariant. To do this, we can write the following two lines of code:

New List<string> ();ienumerable<object> objects = strings;

The first line, which declares a variable strings of the ienumerable<string> type, assigns a value to the object that implements the Ienumerable<t> List<string> class. The second line assigns the strings to a variable objects of type ienumerable<object>.

This may seem logical, because the string type is a worry-free conversion to the object type. So,ienumerable<string> to Ienumerable<object> is also a worry-free conversion?

But wait a minute!

String==>object no problem, is not to explain ienumerable<string>==>ienumerable<object> no problem! Because, String derives from Object, but,ienumerable<string> is not derived from ienumerable<object> Be sure to figure out whether it's enumerable<string>, or ienumerable<object>, that is derived from Object. There is no derivation between him and two.

Since there is no derivation, what is this conversion to be possible?

As we said earlier, interfaces are actually a collection of methods. and entrust it? Represents a method. Is there some kind of connection? Yes, there are connections, and even when discussing the concept of covariance and contravariance, interfaces and delegates can actually be understood in the same way.

An interface that defines a variable type parameter is actually a method of passing these variable type parameters to the interface, and these individual methods, when discussing the concept of covariance and contravariance, can be regarded as delegates, nothing wrong.

Speaking of which, smart you should not need me to continue to nag, calm down to think about it, you will understand the commission of the covariance and the inverse of what is going on.

The type parameters of a generic interface, such as t above, are used by methods passed to the interface definition.

Define a covariant type parameter T, which is the method that tells the interface, the type of T is covariant, and you can replace the original type with a type that is more toward the parent class. In the statement ienumerable<object> objects = Strings , the string type is replaced with the Object type. In general, because the covariant type parameter is converted toward the parent class type, it is available for the return value of the interface method. It is no problem for you to return a string to object.

Similarly, defining an inverse type parameter tells the interface method that the type is contravariant, and you can replace the original type with a type that is more subclass-oriented. In general, the type parameter of the contravariant can be used to define the parameters of the method, but not for the return value. This place has a very good analysis of people, it does not unfold, refer to the above generic delegate in the covariance and inverse of the relevant content of the discussion on the understanding.

Summarize

In general, covariant generic type parameters can be used for the return value type of a delegate, or the return value type of a method in an interface. The inverse of the generic type parameter, the parameter type that can be used for the delegate, or the method of the interface. Note that covariance and contravariance refer to the type, not the object, which is the key to the problem, and a lot of people are confused and confusing here .

Say so much, there is no place in the text, please the people to criticize.

The covariance and inversion of the generic parameters are clarified

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.