Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   Delegate and Interface Contra-variant and Covariant Generic Type Arguments

Each of a delegate’s generic type parameters can be marked as covariant or contra-variant. This feature allows you to cast a variable of a generic delegate type to the same delegate type where the generic parameter types differ. A generic type parameter can be any one of the following:

InvariantMeaning that the generic type parameter cannot be changed. I have shown only invariant generic type parameters so far in this chapter.

Contra-variantMeaning that the generic type parameter can change from a class to a class derived from it. In C#, you indicate contra-variant generic type parameters with the in keyword. Contra-variant generic type parameters can appear only in input positions such as a method’s argument.

CovariantMeaning that the generic type argument can change from a class to one of its base classes. In C#, you indicate covariant generic type parameters with the out keyword. Co- variant generic type parameters can appear only in output positions such as a method’s return type.

For example, let’s say that the following delegate type definition exists (which, by the way, it does).

 

public delegate TResult Func<in T, out TResult>(T arg);

 

Here, the generic type parameter T is marked with the in keyword, making it contra-variant; and the generic type parameter TResult is marked with the out keyword, making it covariant.


So now, if I have a variable declared as follows.

 

Func<Object, ArgumentException> fn1 = null;

 

I can cast it to another Func type, where the generic type parameters are different.

 

Func<String, Exception>fn2 = fn1; // No explicit cast is required here Exception e = fn2("");

 

What this is saying is that fn1 refers to a function that accepts an Object and returns an ArgumentException. The fn2 variable wants to refer to a method that takes a String and re- turns an Exception. Because you can pass a String to a method that wants an Object (because String is derived from Object), and because you can take the result of a method that returns

an ArgumentException and treat it as an Exception (because Exception is a base class of ArgumentException), the code above compiles and is known at compile time to preserve type safety.

 

NoteVariance applies only if the compiler can verify that a reference conversion exists be- tween types. In other words, variance is not possible for value types because boxing would

be required. In my opinion, this restriction is what makes these variance features not that useful. For example, if I have the following method.

void ProcessCollection(IEnumerable<Object> collection) { ... }

 

I can’t call it passing in a reference to a List<DateTime> object because a reference con- version doesn’t exist between the DateTime value type and Object even though DateTime is derived from Object. You solve this problem by declaring ProcessCollection as follows.

void ProcessCollection<T>(IEnumerable<T> collection) { ... }



 

Plus, the big benefit of ProcessCollection(IEnumerable<Object> collection) is that there is only one version of the JITted code. However, with ProcessCollection<T>­ (IEnumerable<T>collection), there is also only one version of the JITted code shared by all Ts that are reference types. You do get other versions of JITted code for Ts that are value types, but now you can at least call the method passing it a collection of value types.

Also, variance is not allowed on a generic type parameter if an argument of that type is passed to a method by using the out or ref keyword. For example, the line of code be- low causes the compiler to generate the following error message: Invalid variance: The type parameter 'T' must be invariantly valid on 'SomeDelegate<T>.

Invoke(ref T)'. 'T' is contravariant.

 

delegate void SomeDelegate<in T>(ref T t);

 

When using delegates that take generic arguments and return types, it is recommended to always specify the in and out keywords for contra-variance and covariance whenever possible, because do- ing this has no ill effects and enables your delegate to be used in more scenarios.


Like delegates, an interface with generic type parameters can have its type parameters be contra- variant or covariant. Here is an example of an interface with a covariant generic type parameter.

 

public interface IEnumerator<out T> : IEnumerator { Boolean MoveNext();

T Current { get; }

}

 

Because T is covariant, it is possible to have the following code compile and run successfully.

 

// This method accepts an IEnumerable of any reference type Int32 Count(IEnumerable<Object> collection) { ... }

 

...

// The call below passes an IEnumerable<String> to Count Int32 c = Count(new[] { "Grant" });

       
   
 
 

 


Date: 2016-03-03; view: 729


<== previous page | next page ==>
Nbsp;   Generics Infrastructure | Nbsp;   Generic Methods
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.007 sec.)