Here is a really good example of where your assumptions about the type of return start to go crazy.
public class Bar { public string Foo(string value) { return value; } }
As you can see here, Bar explicitly 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 have created a derived class that also implements IDynamicMetaObjectProvider . This is the interface that C # uses to get a DynamicMetaObject that defines how dynamic calls are connected 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 overload of DynamicMetaObject 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 did this ...
dynamic value = "Hello, world!"; Bar bar = new Baz(); var returnValue = bar.Foo(value);
... the returnValue value at run time will be 1234 , not "Hello, world!" .
Now, in the real world, this is insanely evil. Although you can completely restore functions that are expected to do something in a certain way, doing something funny like this will only confuse people along the way. This, as they say, is quite possible in the CLR.
TL; DR: When you use dynamic , you cannot always be sure what you think is right.