Inverter and collaborative change

Source: Internet
Author: User

Inverter and collaborative change
Inverter and covariant are not clear. Collect them first to facilitate later learning. Explanation of inverter and covariant

Contravariant and covariant are new concepts in C #4. Many books and blogs have explained them. I don't think I have made them clear and understood them, you can more accurately define generic delegation and interfaces. Here I try to draw a picture to analyze the inverter and covariant in detail.

Concept of change

We all know that in. Net or in the OO world, we can safely assign the reference of the subclass to the parent class, for example:

 

// Parent class = subclass

String str = "string ";

Object obj = str; // changed

C # has the concept of generics, which further abstracts the type system and is more advanced than the simple type above, the above changes are embodied in generic parameters, which are what we call the concept of inverter and covariant. By using the in or out keyword on generic parameters, you can obtain the inverter or covariant capability. Below are some examples of comparison:

Covariant (Foo <parent class> = Foo <subclass> ):

 

// Generic delegation:

Public delegate T MyFuncA <T> (); // inverter and covariant are not supported

Public delegate T MyFuncB <out T> (); // supports covariant

 

MyFuncA <object> funcAObject = null;

MyFuncA <string> funcAString = null;

MyFuncB <object> funcBObject = null;

MyFuncB <string> funcBString = null;

MyFuncB <int> funcBInt = null;

 

FuncAObject = funcAString; // compilation failed. MyFuncA does not support inverter and covariant

FuncBObject = funcBString; // changed, covariant

FuncBObject = funcBInt; // compilation failed. value types do not participate in covariant or inverter.

 

// Generic interface

Public interface IFlyA <T >{} // inverter and covariant are not supported

Public interface IFlyB <out T >{} // supports covariant

 

IFlyA <object> flyAObject = null;

IFlyA <string> flyAString = null;

IFlyB <object> flyBObject = null;

IFlyB <string> flyBString = null;

IFlyB <int> flyBInt = null;

 

FlyAObject = flyAString; // compilation failed. IFlyA does not support inverter and covariant.

FlyBObject = flyBString; // changed, covariant

FlyBObject = flyBInt; // compilation failed. The value type does not participate in covariant or inverter.

 

// Array:

String [] strings = new string [] {"string "};

Object [] objects = strings;

Invert (Foo <subclass> = Foo <parent class>)

 

Public delegate void MyActionA <T> (T param); // inverter and covariant are not supported

Public delegate void MyActionB <in T> (T param); // supports Inverter

 

Public interface IPlayA <T >{} // inverter and covariant are not supported

Public interface IPlayB <in T >{} // supports Inverter

 

MyActionA <object> actionAObject = null;

MyActionA <string> actionAString = null;

MyActionB <object> actionBObject = null;

MyActionB <string> actionBString = null;

ActionAString = actionAObject; // MyActionA does not support inverter and covariant, and compilation fails.

ActionBString = actionBObject; // changed, Inverter

 

IPlayA <object> playAObject = null;

IPlayA <string> playAString = null;

IPlayB <object> playBObject = null;

IPlayB <string> playBString = null;

PlayAString = playAObject; // IPlayA does not support inverter and covariant, and compilation fails.

PlayBString = playBObject; // changed, Inverter

Here we can see that some can be changed, and some can not be changed, you need to know the following points:

  • In the past, generic systems (or when there is no in/out keyword) cannot be changed, whether it is "inverse" or "SHUN (Co-operation )".
  • Currently, only interfaces and delegated inverters and covariant are supported, and classes and methods are not supported. However, arrays also have covariance.
  • Value types do not participate in inverter and covariant.

So what does in/out mean? Why do we have the ability to "change" when we add them? Do we need to add them to define generic delegation or interfaces?

It turns out that if the in keyword is added to a generic parameter as the wildcard modifier, that generic parameter can only be used as the input parameter of the method, or only write the attribute parameter, it cannot be used as the return value of a method. In short, it can only be "in" and cannot be used as an output. Otherwise, the out keyword.

When you try to compile the following generic interface that uses the in generic parameter as the method return value:

 

Public interface IPlayB <in T>

{

T Test ();

}

The following compilation error occurs:

Error 1 variance is invalid: The type parameter "T" must be a valid covariant on "CovarianceAndContravariance. IPlayB <T>. Test. "T" is an inverter.

At this point, we have roughly understood the concepts related to inverter and covariant. Why can we "change" by limiting generic parameters to in or out? Next we will try to draw a picture to explain the principle.

The Coordinated change is not a matter of course, and the inverter is not "inverse"

Let's take a look at the generics that do not support inverter and covariant, assign the subclass to the parent class, and then execute the specific process of the parent class method. For a simple example of the Test method:

 

Public interface Base <T>

{

T Test (T param );

}

Public class Sub <T>: Base <T>

{

Public T Test (T param) {return default (T );}

}

Base <string> B = new Sub <string> ();

B. Test ("");

The actual process is as follows:

 

That is, the method that calls the parent class is actually the method that calls the subclass. As you can see, this method can be called safely. Two conditions are required: 1. the method parameters of the variant (parent) can be safely converted to the parameters of the original formula (child); 2. the return value of the original formula (sub) can be converted to the return value of the variant safely. Unfortunately, the flow direction of the parameter is the opposite to that of the returned value. Therefore, it is certainly not feasible for both in and out generic parameters, one Direction cannot be converted securely. For example, in the above example, we try to "change ":

 

Base <object> BaseObject = null;

Base <string> BaseString = null;

BaseObject = BaseString; // compilation failed

BaseObject. Test ("");

Here, the "actual process" is as follows. We can see that the object in the parameter cannot be converted to a string safely, so the compilation fails:

 

If we can see it here, it is not difficult to get the "actual flow chart" of the inverter and the covariant (Remember, they have in/out restrictions ):

 

As you can see, from the perspective of the "actual flow chart", there is no "inverse" in the inverter. It is impossible to safely assign the reference of the subclass to the parent class.

Here, we should basically understand the changes in the inverter and association. However, this article assembling the head has a more advanced question, and the original article also provides answers. Here I will understand it by drawing the above picture.

Diagram of the interaction between the inverter and the covariant

Do you know that the question is correct?

 

Public interface IBar <in T> {}

// It should be in

Public interface IFoo <in T>

{

Void Test (IBar <T> bar );

}

// Or out

Public interface IFoo <out T>

{

Void Test (IBar <T> bar );

}

The answer is: if it is in, the compilation will fail and the out will be correct (of course, the wildcard modifier can also be compiled, but IFoo will not be able to be changed ). This means that a wildcard (IBar) with the capability of covariance (inverter), as a parameter of another generic (IFoo), affects it (IFoo). At first glance, I thought it was in. One of the traps is that T is in the parameters of the Test method, so I thought it was in. But here the Test parameter is not T, but IBar <T>.

Let's draw a picture to understand it. Since the out can pass, its "covariant flowchart" should be as follows:

 

The figure is roughly the same as the previous one, but it should be opposite to the problem (the above problem is to define IBar first and then define IFoo ). 1. We define an IFoo with the capability of covariant, which is a prerequisite. 2. The process above can be launched. 3. The focus of this process is the parameter flow direction. To establish the entire process, IBar <string> = IBar <object> must be set up. Isn't this an inverter? The conclusion is,IFoo with covariant capability requires that its generic parameters (IBar) have the inverter capability.. In fact, we can also understand the arrows above, because the original and variant changes are the opposite of the parameter changes, resulting in the opposite ability of them, this is what the Assembly head article says:The principle of covariant-inverse change interchange of method parameters.According to this principle, it is easy to conclude that if the return value of the Test method is IBar <T>, rather than a parameter, IBar <T> must have the capability of covariant, because the return value arrow is the same as the original and variant arrow.

The End!

Related Article

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.