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);
This is mainly useful when using already defined standard interfaces. Covariance means that you can use IEnumerable<string> in place where IEnumerable<object> is expected. Contravariance allows you to pass IComparable<object> as an argument of a method taking IComparable<string> .
Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types.
Covariance permits a method to have return type that is more derived than that defined in the delegate. Contravariance permits a method that has parameter types that are less derived than those in the delegate type.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With