Question

So I have a delegate defined as:

public delegate void MyDelegate<T>(T myParameter);

Resharper suggests that I should make T contravariant as follows:

public delegate void MyDelegate<in T>(T myParameter);

Now, I have a hard time understanding what this is good for? I know it prevents me from making T a return type, but other than that, what useful constraints do I get by making T contravariant? That is, when it's time to use the delegate with an instance, what instances can I create with

public delegate void MyDelegate<T>(T myParameter);

that I cannot create with

public delegate void MyDelegate<in T>(T myParameter);
Was it helpful?

Solution

Here's an instance where if you remove the contravariance in marker it won't compile:

delegate void Callback<in T>(T t);

public Form1()
{
    InitializeComponent();
    Callback<Control> showText = control => MessageBox.Show(control.Text);
    var button = new Button();
    AddButtonClickCallback(button, showText);
    var label = new Label();
    AddLabelClickCallback(label, showText);
}

static void AddButtonClickCallback(Button button, Callback<Button> callback)
{
    button.Click += delegate { callback(button); };
}

static void AddLabelClickCallback(Label label, Callback<Label> callback)
{
    label.Click += delegate { callback(label); };
}

Somewhat contrived, sure, but should at least give you an idea of the kind of things you can't do without it.

Especially think in terms of if AddLabelClickCallback and AddButtonClickCallback were library functions and Callback was a library delegate. If it was defined without contravariance, you'd have to define different delegates showButtonText and showLabelText even though you just want them to do the same thing.

OTHER TIPS

The in keyword on generic allows for implicit conversion to occur. Basically you can assign less derived delegate types to your delegate... Not always useful, read here for more information.

http://msdn.microsoft.com/en-us/library/dd469484.aspx

Example, from MSDN article:

// Contravariant delegate. 
public delegate void DContravariant<in A>(A argument);

// Methods that match the delegate signature. 
public static void SampleControl(Control control)
{ }
public static void SampleButton(Button button)
{ }

public void Test()
{
    // Instantiating the delegates with the methods.
    DContravariant<Control> dControl = SampleControl;
    DContravariant<Button> dButton = SampleButton;

    // You can assign dControl to dButton 
    // because the DContravariant delegate is contravariant.
    dButton = dControl;

    // Invoke the delegate.
    dButton(new Button()); 
}

In that example, the Control will be implicitly converted to Button type, though an exception could occur if the implicit conversion to Button type was not defined in the Button class to allow Control to become Button.

More on implicit conversion definitions: http://msdn.microsoft.com/en-us/library/z5z9kes2.aspx

This has been asked many times on stackoverflow: Covariance and contravariance in programming languages

I suggest you go through: http://blogs.msdn.com/b/ericlippert/archive/2007/10/22/covariance-and-contravariance-in-c-part-four-real-delegate-variance.aspx

Covariance and Contravariance in C#, Part One
Covariance and Contravariance in C#, Part Two: Array Covariance
Covariance and Contravariance in C#, Part Three: Method Group Conversion Variance
Covariance and Contravariance in C#, Part Four: Real Delegate Variance
Covariance and Contravariance In C#, Part Five: Higher Order Functions Hurt My Brain
Covariance and Contravariance in C#, Part Six: Interface Variance
Covariance and Contravariance in C# Part Seven: Why Do We Need A Syntax At All?
Covariance and Contravariance in C#, Part Eight: Syntax Options
Covariance and Contravariance in C#, Part Nine: Breaking Changes
Covariance and Contravariance in C#, Part Ten: Dealing With Ambiguity
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top