tags:

views:

94

answers:

2

The following example is taken from C# in Depth: What you need to master C# 2 and 3, and seems to only only cause a breaking change as jskeet has identified, but be wrong. Please explain:

delegate void SampleDelegate(string x);

 public void CandidateAction (string x)
 {
     Console.WriteLine("Snippet.CandidateAction")
 }

public class Derived: Snippet
{
   public void CandidateAction (object x)
   {
     Console.WriteLine("Derived.CandidateAction")
   }
}
....

Derived x = new Derived();
SampleDelegate factory = new SampleDelegate (x.CandidateAction);
factory ("test");

Now, why should it work altogether as SampleDelegate accept string not the object. And to my knowledge, object doesn't derive from the string. It is the other way around. That's what contravariance permits under c# 2.0. The seems to demonstrate the opposite effect.

Thanks

+3  A: 

Conceptually, Derived.CandidateAction's signature is saying, "I can handle any object you want to throw at me." SampleDelegate's contract is "You have to be able to handle a string." Now if a method can handle any object, it can certainly handle a string. So Derived.CandidateAction is capable of fulfilling what SampleDelegate needs, and can therefore be assigned to a SampleDelegate variable.

I wrote a more detailed discussion of this (admittedly from a C# 4 point of view) at http://hestia.typepad.com/flatlander/2008/12/c-covariance-and-contravariance-by-example.html.

itowlson
+1  A: 

Contravariance allows you to use a method which has parameters which are a base type of the parameters in the delegate method signature.

The delegate signature defines what types will be passed to the method. The method itself can have types that are less "specific". In your example, when the delegate is invoked as string is being passed. It is perfectly ok for the actual method to only want an object because a variable of type object is allowed to hold an instance of type string.

This is most useful in event handling scenarios where you can write an event handler like this that can be attached to almost any event (even when the args parameter being passed is more specific like ItemCheckedEventArgs):

public void GenericHandler(object source, EventArgs args) {
    //do something generic
}
Erv Walter