如何直接调用DynamicObject.TryGetMember?

Nés*_* A. 26 .net dynamic

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

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");
}
Run Code Online (Sandbox Code Playgroud)

是否已经存在GetMemberBinder已经存在的具体后代?或者是创建我自己的实现的指南?

Aar*_*ght 55

我不确定框架中是否存在实际返回的任何方法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);
}
Run Code Online (Sandbox Code Playgroud)

注意Binder.GetMember创建一个CallSiteBinder,而不是一个GetMemberBinder.只是要100%清楚.RuntimeBinderException如果内部调用TryGetMember失败,此方法将抛出一个,因此您不需要检查结果.如果您不希望调用者看到RuntimeBinderException它,请将其包装在您自己的try/catch中.

动态调度很复杂,至少相对于静态类型的反射.由于CLR实际上不是动态类型的,因此C#必须实际实例化编译器以确定如何执行成员/方法.那就是创建一个呼叫站点.据我所知,你必须这样做,这就是为什么每个Binder方法返回一个CallSiteBinder,你不能直接实例化任何绑定器.

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

PS如果您正在使用(或可以使用)ExpandoObject而不是DynamicObject记住它实现IDictionary<string, object>,那么您不需要执行任何此操作.只需将其强制转换为字典类型并检查属性是否存在.我只会曾经使用DynamicObjectExpandoObject,如果我正在做的东西很多不是简单地在运行时添加成员,即改变基础上,运行时绑定的实际行为更加复杂.


jbt*_*ule 14

你不直接调用TryGetMember,你需要的是使用动态api直接通过使用csharp成员绑定器和调用站点来获得相同的效果.

开源框架Dynamitey(通过nuget)使这变得更加容易,因为它有一个静态方法来执行此操作.它适用于任何IDynamicMetaObjectProvider,而不仅仅是DynamicObject和(它适用于比反射更快的常规类型).

return Dynamic.InvokeGet(Source, FieldName);
Run Code Online (Sandbox Code Playgroud)

  • 我不明白为什么这个答案被否决了.现在确实如此,不是吗? (3认同)