Why does the following example using covariance in delegates not compile?

c#, covariance, delegates, generics

Solution

Delegate types are still concrete object types. When you write

delegate object delobject();
delegate string delstring();

then there is no "is a" relation between the two delegate types. You've simply created two distinct types.

It's just like

class A { public int i; };
class B { public int i; };

There is no implicit or explicit conversion between `A` and `B`, even though there isn't any possible `A` that wouldn't make equal sense as a `B`, or vice versa.

Co- and contravariance in `Func` means that the authors of that concrete delegate type have decided that `Func<string>` may be treated as `Func<object>`, but that's something the author of the delegate type gets to decide, just like it's the author of my class `B` that gets to decide whether it would perhaps make more sense to just derive from `A`.

Something you can do is add one more level of indirection without creating an additional method:

delstring a = () => "foo";
delobject b = new delobject(a); // or equivalently, a.Invoke

Problem

I have defined the following delegate types. One returns a string, and one an object: ``` delegate object delobject(); delegate string delstring(); ``` Now consider the following code: ``` delstring a = () => "foo"; delobject b = a; //Does not compile! ``` Why is the assignment not valid ? I do not understand. A method which returns a string should be safely considered as a method which returns an object (since a string is an object). In C# 4.0, the following example works. Instead of using delegates, I use the `Func<TResult>` generic type: ``` Func<string> a = () => "foo"; Func<object> b = a; //Perfectly legal, thanks to covariance in generics ``` Also: if I rewrote it that way, it works: ``` delobject b = () => a(); ``` But this is not the same thing as what I wanted initially. Now I have created a new method which calls another one. It is not just an assignment, as shown in this example: ``` delint a = () => 5; delobject b = a; //Does not work, but this is OK, since "int" is a value type. delobject b = () => a(); //This will box the integer to an object. ```

Original source