Consider the following:
using System;
using System.Dynamic;
namespace DynamicTest
{
class Program
{
static void Main(string[] args)
{
dynamic d = new DynamicTest();
var v = d.foo();
Console.WriteLine(v.Value);
Console.ReadKey();
}
}
public interface IValueProvider<T>
{
T Value
{
get;
}
}
public class DynamicTest : DynamicObject
{
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
result = new ValueProvider<int>(32);
return true;
}
private class ValueProvider<T> : IValueProvider<T>
{
public ValueProvider(T t)
{
this.Value = t;
}
public T Value
{
get;
private set;
}
}
}
}
This fails at runtime with:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was unhandled
Message='object' does not contain a definition for 'Value'
Source=Anonymously Hosted DynamicMethods Assembly
StackTrace:
at CallSite.Target(Closure , CallSite , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at DynamicTest.Program.Main(String[] args)
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
If I change the ValueProvider<T> class to public rather than private it works as expected:
// this allows it to work
public class ValueProvider<T> : IValueProvider<T>
Or, instead, if I cast the result to IValueProvider<int> it also works:
// this also allows it to work
dynamic d = new DynamicTest();
var v = (IValueProvider<int>)d.foo();
If I try using the interface as the compile-time type in TryInvokeMember, it doesn’t work (didn’t really expect it to, but thought I’d try):
// this doesn't help
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
var valueProvider = (IValueProvider<int>)new ValueProvider<int>(32);
result = valueProvider;
return true;
}
Can anyone point out what I’m missing here? Is there a way for me to get the code above working without resorting to making all my types public and without casting?
Interestingly, it works if the accessibility is
internal:which as Hassan notes (comments) makes sense, as that is the accessibility that would normally be required for
Programto use the API members ofDynamicTest(in the same assembly). If we moveMaintoDynamicTestand leave itprivateit again works, which is the correct accessibility we should expect.Maybe use
internalfor now.Of course, an even better approach here would be to just use the interface. IMO you don’t need
dynamichere; what you perhaps need is a non-genericIValueProvider:with (in the class):
and: