Here's a really good example of where your assumptions about return type begin to go awry.
public class Bar
{
public string Foo(string value)
{
return value;
}
}
As you can see here, Bar
clearly has an instance method Foo
that takes a string and returns a string.
public class Baz : Bar, IDynamicMetaObjectProvider
{
public DynamicMetaObject GetMetaObject(Expression parameter)
{
return new BazMetaObject(parameter, this);
}
}
But now I've created a derived class that also implements IDynamicMetaObjectProvider
. This is the interface that C# uses to get a DynamicMetaObject
, which determines how the dynamic calls are bound at runtime. For example:
public class BazMetaObject : DynamicMetaObject
{
public BazMetaObject(Expression expression, Baz value)
: base(expression, BindingRestrictions.Empty, value)
{
}
public override DynamicMetaObject BindInvokeMember(
InvokeMemberBinder binder, DynamicMetaObject[] args)
{
if (binder.Name == "Foo")
{
return new DynamicMetaObject(
Expression.Convert(
Expression.Call(
typeof (BazMetaObject).GetMethod("DynamicFoo")
),
typeof (object)
),
BindingRestrictions.GetTypeRestriction(
this.Expression,
this.LimitType
)
);
}
return base.BindInvokeMember(binder, args);
}
public static int DynamicFoo()
{
return 1234;
}
}
This DynamicMetaObject
overload will capture any calls to Foo
and dynamically redirect them to DynamicFoo
, which has a completely different signature—including that it returns int
, not string
.
So, if you were to do this...
dynamic value = "Hello, world!";
Bar bar = new Baz();
var returnValue = bar.Foo(value);
...the value of returnValue
at runtime would be 1234
, not "Hello, world!"
.
Now, in the real world, this is insanely evil. While it's possible to completely rebind functions that are expected to do something in a certain way, doing something ridiculous like this will only serve to confuse people down the road. That being said, it is completely possible in the CLR.
TL;DR: When you're using dynamic
, you can't always be certain of things you think are correct.