Consider the following example program:
using System;
public delegate string MyDelegateType(int integer);
partial class Program
{
static string MyMethod(int integer) { return integer.ToString(); }
static void Main()
{
Func<int, string> func = MyMethod;
// Scenario 1: works
var newDelegate1 = new MyDelegateType(func);
newDelegate1(47);
// Scenario 2: doesn’t work
dynamic dyn = func;
var newDelegate2 = new MyDelegateType(dyn);
newDelegate2(47);
}
}
The first one works as expected — the conversion to MyDelegateType
succeeds. The second one, however, throws a RuntimeBinderException
with the error message:
Cannot implicitly convert type 'System.Func<int,string>' to 'MyDelegateType'
Is there anything in the C# specification that allows for this behaviour, or is this a bug in Microsoft’s C# compiler?
Good catch Timwi.
Our support for dynamic method groups is weak. For example, consider this simpler case:
class C
{
public void M() {}
}
class P
{
static void Main()
{
dynamic d = new C();
C c = new C();
Action a1 = c.M; // works
Action a2 = d.M; // fails at runtime
The d.M
is interpreted as a property get (or field access) by the dynamic runtime, and when it resolves as a method group, it fails at runtime.
The same thing is happening in your case, it is just a bit more obscure. When you say MyDelegate x = new MyDelegate(someOtherDelegate);
that is treated by the compiler just as if you'd said MyDelegate x = someOtherDelegate.Invoke;
. The dynamic runtime piece does not know to do that transformation, and even if it did, it couldn't handle resolving the method group that is the result of the .Invoke
portion of the expression.
Is there anything in the C# specification that allows for this behaviour, or is this a bug in Microsoft’s C# compiler?
The spec does not call out that this should be a runtime error, and does imply that it should be handled correctly at runtime; clearly the implementation does not do so. Though it is a shortcoming of the implementation I wouldn't call this a "bug" because we deliberately made the behaviour you've discovered. We did not have the resources to make these kinds of expressions work exactly right, so we left them as errors. If we ever get a good way to represent method groups in the dynamic runtime, we might implement it.
Similarly there is no way in dynamic code to represent the notion of "this dynamic thing is a lambda expression where the types of the parameters are to be determined at runtime". If we have a good way to represent those in the future, we might do the work.
Sam talked a bit about this back in 2008; see his article on it:
http://blogs.msdn.com/b/samng/archive/2008/11/02/dynamic-in-c-ii-basics.aspx
I've run in to this limitation too. Although I couldn't answer the why better than Eric Lippert, there is a straight forward workaround.
var newDelegate2 = new MyDelegateType(x => dyn(x));
It implicitly gets the static signature from the delegate and the dynamic invocation works without any more info. This works for delegates and, as a bonus, dynamic callable objects.
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