views:

62

answers:

3
class MyClass
{
    public event Action<string> OnAction;
    public event Func<string, int> OnFunc;
}

class Program
{
    static void Main(string[] args)
    {
        MyClass mc = new MyClass();

        /// I need to handle arbitrary events.
        /// But I see no way to create anonymous delegates in runtime
        /// with arbitrary returns and parameters. So I choosed to
        /// create multiple “signatures” with different parameter
        /// number (up to 10) and different returns (either the Action or
        /// Func). While Actions<> work pretty well, the Funcs<> do not.
        Action<dynamic> someAction = delegate(dynamic p1) { };
        Func<dynamic, dynamic> someFunc = delegate(dynamic p1) { return 42;};

        // OK
        mc.OnAction += someAction;

        // Error: “Cannot implicitly convert type 'System.Func<dynamic,dynamic>'
        // to 'System.Func<string,int>'”
        mc.OnFunc += someFunc;

        // It doesn't work this way as well (the same error message):
        // dynamic someFunc = new Func<dynamic, dynamic>((dynamic n1) => { return 42; });

        // Let's try another way
        // 1:
        // Cannot convert anonymous method to delegate type 'System.Func<string,int>'
        // because the parameter types do not match the delegate parameter types.
        // 2 (even more funny):
        // Parameter 1 is declared as type 'dynamic' but should be 'string'.
        mc.OnFunc += delegate(dynamic p1) { return 42; };
    }
}

Why does it work for actions and doesn't for functions? In other words, I just would like to know why Action<dynamic> → Action<string> is ok while Func<dynamic,dynamic> → Func<string, int> is not. Thanks.

A: 

The only difference between your Action and Func definitions is Action has no return types. If you change your code from:

mc.OnFunc += new Func<dynamic, dynamic>(someFunc);// += someFunc;

to

mc.OnFunc += new Func<dynamic, int>(someFunc);// += someFunc;

It works.

decyclone
But I just can't do that. I need a universal solution.
Entrase
@Entrase, there's no such thing as a universal solution... If you're looking for the universal answer, it's 42 ;)
Thomas Levesque
Let's stay in the context. I'm not looking for meaning of life. I just would like to know why Action<dynamic> → Action<string> is ok while Func<dynamic,dynamic> → Func<string, int> is not. Please tell me if you know.
Entrase
+1  A: 

If C# is expecting a delegate with a return type of int, a delegate declared to return dynamic is incompatible. Your delegate is declared as Func<dynamic, dynamic>, and C# is strongly-typed. It doesn't care if your function actually returns an int and takes a string; it's still declared as Func<dynamic, dynamic>.

The reason why the code doesn't compile is obvious if you consider this example:

Func<string, int> myFunc = delegate(string foo) { return 42; };    
int result = myFunc("Foo");

// If this was allowed...
Func<dynamic, dynamic> otherFunc = delegate(string foo) { return 42; };
myFunc = otherFunc;
result = myFunc("Foo");

// Then this would also be allowed but would be a run-time error.
otherFunc = delegate(string foo) { return "Bar"; }; // Valid
myFunc = otherFunc;
result = myFunc("Foo"); // Oops... an int isn't returned.
Jacob
Why does dynamic binding/casting work for parameters and doesn't work for returns?
Entrase
A: 

Ok, I got it.

The action is defined as follows: delegate void Action <in T> (T arg);, while the func is like this: delegate TResult Func <in T, out TResult> (T arg); The problem is in out keyword, which indicates covariance but not contravariance as in keyword does. At the same time typeof(dynamic) == typeof(object) is true. So dynamic, as a more general type, isn't covariant to whatever we take. Hmm, I thought dynamic binding is more flexible.“

Further reading: http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx

Btw, the quote I found there afterwards:

You can mark a generic type parameter as covariant if it is used only as a method return type and is not used as a type of formal method parameters. And vice versa, you can mark a type as contravariant if it is used only as a type of formal method parameters and not used as a method return type.

Entrase