Generic covariant and reverse variant in. NET 4.0

Source: Internet
Author: User

C #4 and VB10, which appeared with Visual Studio 2010 CTP, have gone through two quite different ways to support new language features: C # focuses on adding some features of later binding and Dynamic Language compatibility. VB10 focuses on simplifying the language and improving the abstract ability. However, both of them have added a function: covariant and contravariant ). Many people may only understand the added in/out keywords, but may not know many of their features. Next we will give a detailed explanation to help you use this feature correctly.

Background: covariant and reverse Variant
Many may not be able to fully understand these terms from physics and mathematics. We don't need to understand their mathematical definitions, but at least we should be able to distinguish between covariance and inversion. In fact, this word comes from the binding between types. We start from the array. An array is actually a type bound to a specific type. The array type Int32 [] corresponds to the original Int32 type. All types of T have their corresponding array type T []. The problem arises. If there is a safe implicit conversion between two types of T and U, is there such conversion between the corresponding array types T [] and U? This involves the ability to map existing type conversions to their array types, known as Variance )". In the. NET world, the only type conversion that allows variability is the "subclass reference-> parent class reference" conversion brought about by the inheritance relationship. For example, the String type inherits from the Object type, so any String reference can be safely converted to an Object reference. We found that the String [] array type references also inherit this conversion capability, which can be converted to Object [] array type references, the same variability as the original type conversion direction is called covariant ).

Because arrays do not support anti-variability, we cannot use array examples to explain the anti-variability. So let's take a look at the variability of generic interfaces and generic delegation. Assume that TSub is a subclass of TParent. Obviously, TSub-type references can be safely converted to TParent-type references. If a generic interface IFoo <T> and IFoo <TSub> can be converted to IFoo <TParent>, we call this process a covariant, in addition, this generic interface supports covariant T. If a generic interface IBar <T> and IBar <TParent> can be converted to T <TSub>, we call this process contravariant ), in addition, this interface supports T inversion. Therefore, it is easy to understand that, if a variability is the same as the conversion direction from a subclass to a parent class, it is called a covariant. If the conversion direction from a subclass to a parent class is the opposite, it is called a non-variant. Do you remember?

Generic covariant and inverse variant introduced by. NET 4.0
When we explained the concept just now, we have used the generic interface for covariant and reverse variant, but in. before NET 4.0, generic variability was not supported in both C # and VB. However, both of them support covariant and reverse variant of the delegate parameter type. Since the variability of the delegate parameter type is more abstract, we are not going to discuss it here. Readers who have fully understood these concepts must be able to understand the variability of delegate parameter types themselves. Before. NET 4.0, why didn't IFoo <T> be allowed for covariant or reverse variant? For the interface, T can be used as either a method parameter or a method return value. Imagine such an Interface

Interface IFoo (Of T)

Sub Method1 (ByVal param As T)

Function Method2 () As T

End Interface
 
Interface IFoo <T>

{

Void Method1 (T param );

T Method2 ();

}
 

If we allow covariant conversion from IFoo <TSub> to IFoo <TParent>, IFoo. Method1 (TSub) is changed to IFoo. Method1 (TParent ). We all know that TParent cannot be converted to TSub safely, so the Method1 method will become insecure. Similarly, if we allow reverse IFoo <TParent> to IFoo <TSub>, TParent IFoo. the Method2 () method is changed to TSub IFoo. method2 (), the originally returned TParent reference may not be converted into a TSub reference, and the call of Method2 will be insecure. It can be seen that, without any additional mechanism restrictions, the type of the interface for covariant or reverse variant is not safe .. What improvements have been made to NET 4.0? It allows you to add an additional description when declaring a type parameter to determine the scope of use of this type parameter. We can see that if a type parameter can only be used for the return value of the function, then this type parameter is compatible with the covariant. On the contrary, if a type parameter can only be used for method parameters, this type parameter is compatible with anti-change. As follows:

Interface ICo (Of Out T)

Function Method () As T

End Interface

 

Interface IContra (Of In T)

Sub Method (ByVal param As T)

End Interface
 
Interface ICo <out T>

{

T Method ();

}

 

Interface IContra <in T>

{

Void Method (T param );

}
 

We can see that C #4 and VB10 both provide the same syntax, and use Out to describe type parameters that can only be used as return values, and In to describe type parameters that can only be used as method parameters. An interface can contain multiple types of parameters, which can be both In and Out. Therefore, we cannot simply say that an interface supports covariant or reverse variant, only one interface supports covariant or reverse variant for a specific type parameter. For example, if an interface such as IBar <in T1, out T2> is available, it supports reverse variation for T1 and covariant for T2. For example, IBar <object, string> can be converted to IBar <string, object>. Here there are both covariant and reverse variant.

In. NET Framework, many interfaces only use type parameters for parameters or return values. For ease of use, these interfaces in. NET Framework 4.0 will be declared as versions that allow covariant or reverse change. For example, IComparable <T> can be declared as IComparable <in T> again, while IEnumerable <T> can be declared as IEnumerable <out T> again. However, some interfaces, IList <T>, cannot be declared as in or out, and therefore cannot support covariant or reverse variant.

The following are several considerations that are easy to ignore during generic covariant and reverse variant changes:

1. Only generic interfaces and generic delegation support the variability of type parameters. Generic or generic methods do not.

2. The value type does not participate in covariant or reverse variation. IFoo <int> cannot be changed to IFoo <object>, regardless of whether the value type is declared out. Because of the. NET generic type, each value type generates an exclusive closed construction type, which is incompatible with the reference type version.

3. When declaring an attribute, you must note that the type of the read/write attribute is used for both parameters and return values. Therefore, only the read-only attribute can use the out type parameter. Only the write attribute can use the in parameter.

Interaction between covariant and reverse Variant
This is a very interesting topic. Let's look at an example:

Interface IFoo (Of In T)

 

End Interface

 

Interface IBar (Of In T)

Is Sub Test (ByVal foo As IFoo (Of T) correct?

End Interface
 
Interface IFoo <in T>

{

 

}

 

Interface IBar <in T>

{

Void Test (IFoo <T> foo); // correct?

}
 

Can you see the problem with the above Code? I declared in T and used it for the parameters of the method. Everything is normal. Unexpectedly, this Code cannot be compiled! Instead, the code is compiled:

Interface IFoo (Of In T)

 

End Interface

 

Interface IBar (Of Out T)

Sub Test (ByVal foo As IFoo (Of T ))

End Interface
 
Interface IFoo <in T>

{

 

}

 

Interface IBar <out T>

{

Void Test (IFoo <T> foo );

}
 

What? Is it an out parameter, but we need to use it as a method parameter to be valid? It seems a little surprising at the beginning. We need to take some twists and turns to understand this problem. Now we want to consider IBar <string>, which should be able to be converted to IBar <object> collaboratively, because string is a subclass of the object. Therefore, IBar. Test (IFoo <string>) is converted to IBar. Test (IFoo <object> ). When we call this covariant method, an IFoo <object> is passed as the parameter. Think about it. This method is changed from IBar. Test (IFoo <string>), so the IFoo <object> parameter must be changed to IFoo <string> to meet the needs of the original function. IFoo <object> must be converted to IFoo <string>! Instead of covariant. That is to say, if an interface needs to be changed to T, the parameter types of all methods of this interface must support T inversion. Likewise, we can see that if the interface must support T-variant, the parameter types of methods in the interface must support T-variant. This is the principle of covariant-inverse change interchange of method parameters. Therefore, we cannot simply say that the out parameter can only be used for the return value. It can only be used to declare the return value type directly. However, as long as one type supports reverse conversion, the out type parameter can also be used for the parameter type! In other words, apart from directly declaring method parameters, the in parameter can only be used for method parameters by means of types that support covariant, it is not allowed to use the T type as the method parameter. If you want to deeply understand this concept, it may be a bit difficult to understand for the first time. We recommend that you perform more experiments if necessary.

We mentioned the mutual effects of covariant and reverse variant on method parameters. Will the return value of the method have the same problem? We can see the following code:

Interface IFooCo (Of Out T)

 

End Interface

 

Interface IFooContra (Of In T)

 

End Interface

 

Interface IBar (Of Out T1, In T2)

Function Test1 () As IFooCo (Of T1)

Function Test2 () As IFooContra (Of T2)

End Interface
 
Interface IFooCo <out T>

{

}

 

Interface

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.