我正在实现一个通用函数来从任意提供的动态对象中提取值,但不知道如何调用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");
}
是否已经存在GetMemberBinder已经存在的具体后代?或者是创建我自己的实现的指南?
答案 0 :(得分:54)
我不确定框架中是否存在实际返回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
,不 a GetMemberBinder
。只是要100%清楚。如果对RuntimeBinderException
的内部调用失败,此方法将抛出TryGetMember
,因此您无需检查结果。如果您不希望呼叫者看到RuntimeBinderException
,请将其包装在您自己的try / catch中。
动态调度很复杂,至少相对于静态类型的反射。由于CLR实际上不是动态类型的,因此C#必须实际实例化编译器以确定如何执行成员/方法。那就是创建一个呼叫站点。据我所知,你有来执行此操作,这就是为什么每个Binder
方法返回CallSiteBinder
并且你不能直接实例化任何绑定器的原因。
请注意,DLR会进行某种呼叫站点缓存,但我不确定自动缓存是否涵盖了这种情况。您很可能希望保存调用站点以供将来调用,以避免不断重新编译的开销。
P.S。如果您使用(或可以使用)ExpandoObject
代替DynamicObject
,请记住它实现了IDictionary<string, object>
,因此您无需执行任何操作。只需将其强制转换为字典类型并检查属性是否存在。如果我做的事情比在运行时简单地添加成员要复杂得多,即根据运行时绑定器改变实际行为,我只会使用DynamicObject
而不是ExpandoObject
。
答案 1 :(得分:14)
您不直接调用TryGetMember,您需要的是使用动态api直接通过使用csharp成员绑定器和调用站点获得相同的效果。
开源框架Dynamitey(通过nuget)使这变得更加容易,因为它有一个静态方法来执行此操作。它适用于任何IDynamicMetaObjectProvider,而不仅仅是DynamicObject和(它适用于比反射更快的常规类型)。
return Dynamic.InvokeGet(Source, FieldName);