Eventhough the ContravarianteObjectMethod can take any of our classes as input the delegate signature for dContravarC is DContravariant<C> and therefore the method invocation can only accept objects that "is a" C.

To help illustrate this I will assign dContravarB since class B is in the "middle" of the derivation.

The generic type parameter has "out" as a modifier. The "out" declaration can be used if the type parameter is used only as a return type of interface methods and not used as a type of method arguments.

// You can assign contraObj to contraStr because the IContravariant interface is contravariant. contraStr = contraObj;

Since we are dealing with arguments (input parameters) and a string is an object you can assign an instance of IContravariant <Object> to IContravariant<String>.

As I am writing this I recognize that the above statement is not clear, even to me. That is because the example code is insufficient. I based the example code on MSDN code. So, let us continue now with a better example.

Notice that I have instantiated a String and an Object. Before betterContraString is assigned betterContraObj I invoke the "MyMethod" method on each instance of IBetterContravariant twice, once passing a String and then passing an Object.

Notice that betterContraObject.MyMethod accepts a String or an Object and there are no compile time errors. This is because a String "is a" Object.

Notice that betterContraString.MyMethod accepts a String however it does not accept an Object. This is because an Object is NOT a String.

Since betterContraObject accepts Strings (as well as Objects) it can be assigned to betterContraString.

Notice in the example code that after betterContraString is assigned betterContraObject that betterContraString still only accepts Strings as input to MyMethod. This is because betterContrString is still an interface to IBetterContravariant<String>. The interface did not change because of the assignment.

Covariance and contravariance provide a degree of flexibility when matching method signatures with delegate types. Covariance permits a method to have a more derived return type than what is defined in the delegate. Contravariance permits a method with parameter types that are less derived than in the delegate type.

What helps me understand this variance stuff is to base it around the "is a" relationship.

class A {}class B : A{}class C: B{}

A "is a" B => falseA "is a" C => false

B "is a" A => trueB "is a "C => false

C "is a" A => trueC "is a" B => true

Return type of delegate signatures(Covariance)

public delegate A HandlerMethodA();

The return type of the delegate is "A".

Speaking solely about the return type in this example, any method that returns something that "is a" A can be used as the delegate method.

All three of these methods can be used as a delegate method for delegate HandlerMethodC.

The reason is that a variable of type C can be passed to any of these handlers.The delegate signature parameter "is a" delegate method parameter.C "is a" A is true.B "is a" A is true.A "is a" A is true.

Consider another delegate:public delegate void HandlerMethodB(B b);

Only methods that can accept B as the parameter type can be assigned to the delegate HandlerMethodB.

FirstHandler and SecondHandler methods may be assigned to the delegate HandlerMethodB.FirstHandler method can accept B types as well as A and C.SecondHandler method can accept B types as well as C.

ThirdHandler cannot be assigned to delegate HandlerMethodB because there is no overload for ThirdHandler that matches the delegate.ThirdHandler method can only accept C types.Since the delegate signature is of type B, third handler cannot be used.

Regardless of which methods may be assigned to HandlerMethodC or HandlerMethodB the delegate will only accept variables that "are" of the type defined by the delegate's signature.

HandlerMethodC may only be invoked with a parameter of type C.HanlderMethodB may be invoked with a parameter of type C or B.