Reputation: 2661
I defined my class hierarchy as Object->Fruit->Apple
class Fruit : object{}
class Apple : Fruit{}
And created two static methods to work with these classes
static Fruit FruitProcessor(string fruit)
{
return new Fruit();
}
static Apple ApplesProcessor(string apple)
{
return new Apple();
}
Now I declare a delegate without any in, out
keywords
public delegate TResult Funk<T, TResult>(T arg);
And in my code I do the following assignment:
Funk<string, Fruit> myFunc;
myFunc = FruitProcessor; // ok, we match signature exactly
myFunc = ApplesProcessor;// this should not work, but works
Since I did not declare TResult as covariant out
parameter it should not be possible to assign ApplesProcessor
to myFunc
delegate. But it is possible, program compiles and executes without any errors.
And if I change Funk signature to add out TResult
public delegate TResult Funk<T, out TResult>(T arg);
Everything works in the same way as before.
How is it possible?
Upvotes: 0
Views: 82
Reputation: 152614
According to MSDN covariance for delegate return types and contravariance for delegate input types is implied since .NET 3.5. So adding in
and out
to your delegate makes no difference.
One reason this was added was so that you could assign a "vanilla" event handler that accepts an EventArgs
parameter to multiple events that may use more specific classes that derive from EventArgs
. For example you can use the same delegate to handle a button click and a key press, even though the former passes a MouseEventArgs
parameter while the latter passes a KeyEventArgs
parameter.
Upvotes: 2
Reputation: 63772
Case like this is handled by the usual implicit conversion. Quoting the relevant MSDN page (https://msdn.microsoft.com/en-us/library/dd233060.aspx):
public class First { }
public class Second : First { }
public delegate First SampleDelegate(Second a);
public delegate R SampleGenericDelegate<A, R>(A a);
// Matching signature.
public static First ASecondRFirst(Second first)
{ return new First(); }
// The return type is more derived.
public static Second ASecondRSecond(Second second)
{ return new Second(); }
// The argument type is less derived.
public static First AFirstRFirst(First first)
{ return new First(); }
// The return type is more derived
// and the argument type is less derived.
public static Second AFirstRSecond(First first)
{ return new Second(); }
// Assigning a method with a matching signature
// to a non-generic delegate. No conversion is necessary.
SampleDelegate dNonGeneric = ASecondRFirst;
// Assigning a method with a more derived return type
// and less derived argument type to a non-generic delegate.
// The implicit conversion is used.
SampleDelegate dNonGenericConversion = AFirstRSecond;
// Assigning a method with a matching signature to a generic delegate.
// No conversion is necessary.
SampleGenericDelegate<Second, First> dGeneric = ASecondRFirst;
// Assigning a method with a more derived return type
// and less derived argument type to a generic delegate.
// The implicit conversion is used.
SampleGenericDelegate<Second, First> dGenericConversion = AFirstRSecond;
In short, in your exact case, you are assigning a non-generic delegate to a generic delegate - the implicit conversion is always used in that case. To actually make your code fail, you need to do something like this:
Funk<string, Fruit> myFunc;
Funk<string, Apple> myAppleFunc = ApplesProcessor;
myFunc = FruitProcessor;
myFunc = myAppleFunc; // Undefined implicit conversion on generic delegate
Upvotes: 2