When I run the following code I get RuntimeBinderException: 'object' does not contain a definition for 'SetIt'
.
public interface IInput { }
public interface IThing<in TInput>
{
void SetIt(TInput value);
}
public class ThingMaker
{
private class Thing<TInput> : IThing<TInput>
where TInput : IInput
{
public void SetIt(TInput value){}
}
private class Input : IInput {}
public IThing<IInput> GetThing()
{
return new Thing<IInput>();
}
public IInput GetInput()
{
return new Input();
}
}
class Program
{
static void Main(string[] args)
{
var thingMaker = new ThingMaker();
var input = thingMaker.GetInput();
dynamic thing= thingMaker.GetThing();
thing.SetIt(input);
}
}
If I switch thing
to a var
it works fine. So clearly, thing
has SetIt
. How come this fails when I use dynamic? Shouldn't any thing that works as var
also work when it is dynamic
?
I think it has something to do with Thing<TInput>
being a private inner class because it works when I make it public.
Here is a simpler example of your problem:
class A
{
class B
{
public void M() { }
}
public static object GetB() { return new B(); }
}
class Program
{
static void Main(string[] args)
{
dynamic o = A.GetB();
o.M();
}
}
The generics and interfaces are just a distraction.
The problem is that the type B
(or in your case, Thing<IInput>
) is a private class, and so at the call site, cannot resolve to that actual type. Recall that dynamic
simply is an object
variable but where compilation has been postponed until run-time. It isn't intended to let you do things at run-time you wouldn't otherwise be able to do, and "wouldn't otherwise be able to do" is judged based on the accessible type at the call site (which in this case, is object
).
As far as the runtime binder is concerned, the type is simply object
(hence the error message telling you that object
doesn't contain the member you want).
Of course, it is theoretically possible that dynamic
could have been implemented differently, and could do a more in-depth search for valid types it could treat the object as for the purpose of binding members to the object (i.e. implemented interfaces rather than just the object's actual type). But that's just not how it was implemented, and it would have been much more costly to do so (both in terms of the original design and implementation, and of course in terms of run-time cost of code that uses dynamic
).
Related reading (arguably duplicates):
Is this a bug in dynamic?
Is this a hole in dynamic binding in C# 4?
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