I've found a bug (feature?) during learning dynamic in C#. Can anyone explain me, why do I have an exception??
static class Program
{
public static void Main(string[] args)
{
dynamic someObj = ConstructSomeObj((Action)(() => Console.WriteLine("wtf")));
var executer = someObj.Execute;
executer(); // shows "wtf"
someObj.Execute(); // throws RuntimeBinderException
Console.ReadKey();
}
static dynamic ConstructSomeObj(dynamic param)
=> new { Execute = param };
}
Note: typeof both exectuer and someObj is dynamic
So, in C/C++ programming, undefined behavior means when the program fails to compile, or it may execute incorrectly, either crashes or generates incorrect results, or when it may fortuitously do exactly what the programmer intended.
It exists because of the syntax rules of C where a variable can be declared without init value. Some compilers assign 0 to such variables and some just assign a mem pointer to the variable and leave just like that. if program does not initialize these variables it leads to undefined behavior.
In C FAQs this behaviour is defined as: “Anything at all can happen; the standard imposes no requirements. The program may fail to compile, or it may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended.”
In computer programming, undefined behavior (UB) is the result of executing a program whose behavior is prescribed to be unpredictable, in the language specification to which the computer code adheres.
Let's look at following code:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("first");
// works perfectly!!!
dynamic foo = new { x=(Action)(() => Console.WriteLine("ok")) };
foo.x();
// fails
dynamic foo2 = new { x=(object)(Action)(() => Console.WriteLine("ok2")) };
foo2.x();
}
}
dynamic
uses reflection to access objects method and fields and since it cannot know exact types it must rely on type information present in objects on which it operate.
When field x
in anonymous type is properly typed as delegate invocation foo.x()
works because dynamic can see that field value is delegate.
When you use
static dynamic ConstructSomeObj(dynamic param)
{ return new { x = param }; }
to create anonymous class you created class with field x
of type object
(dynamic is object
behind the scenes). When you call obj.x
dynamic sees that field type is an object
and it does't bother to check to what exact type this field points. And since object doesn't have Invoke()
method like delegates it throws exception. If you change method parameter type to Action
it will work.
I guess this decision to check field type instead of type of value that field contains was taken to provide better performance. In other words when you check field type CallSite
class generated by dynamic
can be cached and reused later.
References: https://github.com/mono/mono/blob/ef407901f8fdd9ed8c377dbec8123b5afb932ebb/mcs/class/Microsoft.CSharp/Microsoft.CSharp.RuntimeBinder/Binder.cs
https://github.com/mono/mono/blob/ef407901f8fdd9ed8c377dbec8123b5afb932ebb/mcs/class/Microsoft.CSharp/Microsoft.CSharp.RuntimeBinder/CSharpInvokeMemberBinder.cs
EDIT: Checked this on mono, can somebody verify on VS
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