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

时间:2012-12-05 00:16:14

标签: c# .net dynamic-language-runtime

当我想在C#中的任何对象上动态调用静态定义的(在“编译时确定”意义上的“静态”,而不是“类级别成员”意义上的)方法时,我可以使用反射来获取该方法的句柄并调用它:

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

但是,通过从DynamicObject继承而变为动态的对象使用TryInvokeMember响应(未定义的)实例方法调用,并且由于显而易见的原因,类不响应的动态方法不会通过反射公开。这意味着我无法获得应由TryInvokeMember响应的方法的方法句柄。

具有讽刺意味的是,在我看来,您无法像在dynamic对象上调用已定义的方法那样轻松地动态调用dynamic对象上的动态方法。

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

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

2 个答案:

答案 0 :(得分:14)

我有一个开源(Apache许可证)框架Dynamitey(在nuget中可用),它封装了动态绑定器代码,这包括自动缓存调用站点。它也为每种类型的活页夹提供了方便的方法(getter,setter,事件,索引器,操作符,转换),但具体你想要InvokeMember

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

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

答案 1 :(得分:9)

实现它的一种方法是模仿C#编译器为动态对象上的方法调用输出的内容。这需要在[EditorBrowsable(EditorBrowsableState.Never)]命名空间中使用标记为Microsoft.CSharp.RuntimeBinder的一堆类型,因此它们在Intellisense中不可见。毋庸置疑,这似乎不是支持的方案,因此使用它需要您自担风险!

此代码调用动态Bar方法,而不对源自DynamicObject的类的实例有任何参数:

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);

This博客帖子和this one有更多关于呼叫网站和活页夹的详细信息。