如何在动态对象上动态调用方法?

18

当我想在C#中动态调用一个静态定义的方法("静态"是指在编译时确定,而不是类级成员),我可以使用反射来获取该方法的句柄并调用它:

typeof(Foo).GetMethod("Bar").Invoke(foo, new object[] { /* params */ });

然而,通过继承DynamicObject使对象动态化后,对于(未定义的)实例方法调用将使用TryInvokeMember进行响应,并且类所响应的动态方法不会通过反射公开,这是显而易见的原因。这意味着我无法获取应该由TryInvokeMember响应的方法的方法句柄。

因此,具有讽刺意味的是,在dynamic对象上动态调用动态方法似乎并不像在非dynamic对象上调用已定义方法那样容易。

我考虑直接调用TryInvokeMember,但第一个参数必须是InvokeMemberBinder的实例,这是一个抽象类。我认为,如果我必须实现一个类来调用动态对象上的动态方法,则一定做错了什么。

如何通过名称调用dynamic对象上的方法,知道目标类没有实现它,并且应该使用TryInvokeMember响应?

2个回答

15

我有一个开源(采用Apache许可证)框架Dynamitey(在nuget上可用),它封装了动态绑定器代码,包括自动缓存调用站点。它还为每种类型的绑定器提供了方便的方法(getter、setter、事件、索引器、运算符、转换),但特别是您想要InvokeMember

当调用类的静态定义(在编译时)成员时,动态绑定器代码实际上比反射(分摊)运行得更快。

Dynamic.InvokeMember(foo,"Bar",arg...);

太棒了!NuGet非常好用!完美运行!谢谢你!应该标记为答案! - CodeHacker
我正在使用类似于 Dynamic.InvokeGet(o.xp, "Product-Group") 的东西,但如果 Product-Group 不存在,则会抛出错误,您有什么处理方法吗? - rahularyansharma

9
有一种方法是模仿C#编译器在动态对象上的方法调用输出。这需要使用一堆标有[EditorBrowsable(EditorBrowsableState.Never)]的类型,它们位于Microsoft.CSharp.RuntimeBinder命名空间中,因此在Intellisense中不会显示出来。不用说,这似乎不是一个受支持的场景,所以请自行决定是否使用!
以下代码在从DynamicObject派生的类的实例上调用了不带任何参数的动态Bar方法:
dynamic dynamicObject = new DerivedFromDynamicObject();
var callSiteBinder = Binder.InvokeMember(CSharpBinderFlags.None, "Bar", Enumerable.Empty<Type>(), typeof(Program),
    new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) });
var callSite = CallSite<Action<CallSite, object>>.Create(callSiteBinder);
callSite.Target(callSite, dynamicObject);

这篇博客文章和这篇有更多关于调用站点和绑定器的细节。


听起来很有趣。我会等待看看是否有人提供了支持的解决方案,因为当这个停止工作时,我可能已经不在了。 - zneak
1
另一方面,由于编译器已经这样做了,它不太可能停止工作,因为那将会使任何使用今天构建的“dynamic”的应用程序失效。 - zneak
@zneak 对,我认为这是一个相当安全的赌注。只是令人惊讶的是,他们竟然费尽心思确保类型被隐藏。 - Trillian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接