2011-03-14 41 views
23

我正在实现一个通用函数来从任意提供的动态对象中提取一个值,但不知道如何调用TryGetMember,因为它需要一个抽象的GetMemberBinder,因此我无法创建它。 样本...如何直接调用DynamicObject.TryGetMember?

public object GetValue(DynamicObject Source, string FieldName) 
{ 
    object Result = null; 
    GetMemberBinder Binder = x; // What object must be provided? 
    Binder.Name = FieldName; 
    if (Source.TryGetMember(Binder, out Result)) 
     return Result; 

    throw new Exception("The field '" + FieldName + "' not exists"); 
} 

有GetMemberBinder的已经存在的具体后代准备就绪?或创建我自己的实施指南?

+0

我必须缺少一些东西...是不是这个完整的方法正是动态赋值运算符已经做了什么,除了不太可靠? – Aaronaught 2011-08-18 03:40:07

+5

我的问题是关于获取动态对象的字段值,而不知道在合适的时间如何命名该字段。所以,我不能编码,例如,“var MyValue = TheDynamicObject.TheField;”因为只有在运行时才能获得字段名称。我正在编写一个通用类来处理外部提供的动态对象。 – 2011-08-18 06:09:42

回答

47

我不知道是否有在实际返回一个GetMemberBinder框架的任何方法,但它并不重要 - 那就是叫不上名字来调用一个动态成员的正确方法。

你实际需要做的是创建一个呼叫站点。该方法是这样的:

static object GetDynamicMember(object obj, string memberName) 
{ 
    var binder = Binder.GetMember(CSharpBinderFlags.None, memberName, obj.GetType(), 
     new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); 
    var callsite = CallSite<Func<CallSite, object, object>>.Create(binder); 
    return callsite.Target(callsite, obj); 
} 

注意Binder.GetMember创建CallSiteBinder一个GetMemberBinder。只是要100%清楚。如果对TryGetMember的内部调用失败,则此方法将抛出RuntimeBinderException,因此您不需要检查结果。如果您不想让来电者看到RuntimeBinderException,然后将其包装在您自己的尝试/接收中。

动态分派是复杂的,至少相对于静态类型的反映。由于CLR实际上并没有动态输入,所以C#必须实例化一个编译器来找出如何执行成员/方法。这是创建一个呼叫站点。据我所知,你做到这一点,这就是为什么每个Binder方法返回CallSiteBinder,你不能直接实例化任何绑定。

请注意,DLR执行某种呼叫站点缓存,但我不确定自动缓存是否涵盖此场景。很有可能您希望保存您的呼叫站点以供将来调用,以避免不断重新编译的开销。

P.S.如果您正在使用(或可以使用)ExpandoObject而不是DynamicObject,请记住它实现了IDictionary<string, object>,因此您不需要执行任何操作。只需将其转换为字典类型并检查属性是否存在。如果我在做一些比在运行时简单添加成员更复杂的事情,即基于运行时绑定器改变实际行为,我只会使用DynamicObject而不是ExpandoObject

+0

自动缓存存储在callsite和callsitebinder中,所以如果你至少保留其中一个,你会得到很差的性能(我测试过)。这是[impromptuinterface](http://code.google.com/p/impromptu-interface/)为您所做的事情之一,它会重新使用它创建的调用网站。 – jbtule 2011-08-18 14:45:16

+0

你当然知道很多关于这个主题的内容,但是如果你不断发布那个链接,你会[开始获取垃圾邮件标志](http://stackoverflow.com/faq#promotion)。如果它并不总是一个超链接,那么至少它会减少羽毛。 – Aaronaught 2011-08-18 15:04:46

+0

我会记住这一点,但它是一个开源框架,旨在解决这个在一般情况下不是微不足道的特定问题。这种情况可能不重要,因为get成员可以始终具有相同的callsite func签名,所以您只需要一个由membername索引的电话字典来解决此缓存问题。 – jbtule 2011-08-18 15:18:32

11

你不直接调用TryGetMember,你需要的是直接使用动态api通过使用csharp成员联编程序和调用站点来获得相同的效果。

开放源代码框架Dynamitey(通过nuget)使这变得更加容易,因为它有一个静态方法来完成此操作。它适用于任何IDynamicMetaObjectProvider,不仅仅适用于DynamicObject,(它适用于常规类型,速度也比反射速度快)。

return Dynamic.InvokeGet(Source, FieldName); 
+2

我不明白为什么这个答案被拒绝。它现在确实如此吗? – 2013-05-24 08:25:18

+0

@jbtule,关于你的用户图片...不错!最佳档案? – 2015-05-29 18:44:58