DynamicObject 绑定 WRT 私有类型
考虑以下情况:
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;
}
}
}
}
这在运行时失败:
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:
如果我将 ValueProvider
类更改为 public
而不是 private
,它会按预期工作:
// this allows it to work
public class ValueProvider<T> : IValueProvider<T>
或者,相反,如果我将结果转换为 IValueProvider
它也可以工作:
// this also allows it to work
dynamic d = new DynamicTest();
var v = (IValueProvider<int>)d.foo();
如果我尝试使用该接口作为 TryInvokeMember
中的编译时类型,它不会工作(没想到,但我想我会尝试):
// 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;
}
任何人都可以指出我在这里缺少什么吗?有没有办法让我在不公开所有类型且不进行强制转换的情况下使上面的代码正常工作?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
有趣的是,如果可访问性是内部的,它就可以工作:
正如 Hassan 所说(评论),这是有道理的,因为这是
Program
使用 API 通常需要的可访问性DynamicTest
的成员(在同一程序集中)。如果我们将Main
移至DynamicTest
并将其保留为private
,它会再次起作用,这是我们应该期望的正确可访问性。也许现在使用
internal
。当然,更好的方法是仅使用接口。 IMO 你在这里不需要需要
动态
;您可能需要的是一个非通用的IValueProvider
:with (in the class):
and:
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
Program
to use the API members ofDynamicTest
(in the same assembly). If we moveMain
toDynamicTest
and leave itprivate
it again works, which is the correct accessibility we should expect.Maybe use
internal
for now.Of course, an even better approach here would be to just use the interface. IMO you don't need
dynamic
here; what you perhaps need is a non-genericIValueProvider
:with (in the class):
and:
一种解决方案是让
ValueProvider
也继承DynamicObject
,然后在其上实现TryGetMember
:当然,在本例中我们是无论您尝试获取什么属性,都只是返回值,但我可能会考虑使用字典或 ExpandoObject 来保留
ValueProvider
上的可用属性One solution would be to make
ValueProvider<T>
inheritDynamicObject
aswell, and then implementingTryGetMember
on that:of course in this example we're just returning the Value no matter what property you try to get, but I'd probably look into using a dictionary or an ExpandoObject to keep the available properties on
ValueProvider<T>