How to call DynamicObject.TryGetMember directly?
I'm implementing a general purpose function to extract a value from an arbitrary provided dynamic object, bu开发者_StackOverflowt don't know how to call TryGetMember
because it requires a GetMemberBinder
which is abstract, hence I cannot create it.
Sample...
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");
}
Is there an already existent concrete descendant of GetMemberBinder ready for use? or a guideline to create my own implementation?
I'm not sure if there's any method in the framework that actually returns a GetMemberBinder
, but it doesn't matter - that isn't the correct way to invoke a dynamic member by name.
What you actually need to do is create a call site. The method looks like this:
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);
}
Note that Binder.GetMember
creates a CallSiteBinder
, not a GetMemberBinder
. Just to be 100% clear. This method will throw a RuntimeBinderException
if the internal call to TryGetMember
fails, so you do not need to check the result. If you don't want callers to see the RuntimeBinderException
then wrap it in your own try/catch.
Dynamic dispatch is complex, at least relative to reflection on static types. Since the CLR is not actually dynamically typed, C# has to actually instantiate a compiler to figure out how to execute the member/method. That's creating a call site. As far as I know, you have to do this, which is why every Binder
method returns a CallSiteBinder
and you can't instantiate any of the binders directly.
Note that the DLR does some sort of call site caching, but I'm not sure if the automatic caching covers this scenario. There's a good chance you'll want to save your call site for future calls to avoid the overhead of constant recompilation.
P.S. If you are using (or can use) ExpandoObject
instead of DynamicObject
then keep in mind that it implements IDictionary<string, object>
, so you don't need to do any of this. Just cast it to the dictionary type and check if the property exists. I would only ever use DynamicObject
over ExpandoObject
if I were doing something a lot more complicated than simply adding members at runtime, i.e. changing the actual behaviour based on the runtime binder.
You don't call TryGetMember directly, what you need is to use the dynamic api's directly to get the same effect by using a csharp member binder and a call site.
This is made even easier by open source framework Dynamitey (via nuget) as it has a static method that does this. It works for any IDynamicMetaObjectProvider not just DynamicObject and (it works for regular types faster than reflection too).
return Dynamic.InvokeGet(Source, FieldName);
While Aaronaught's answer explains what might be the "right" way to do something manually that I believe Dynamity already does for you, it does not actually answer the poster's question.
The question asks how to call TryGetMember( ) directly, and others should not pass judgement on the propriety of the original poster's interest in doing so.
To actually answer the question, there is only one abstract member, not counting the constructor. In my application, the following concrete class is sufficient, and I believe the most efficient solution:
public class GetMemberBinder : System.Dynamic.GetMemberBinder
{
public GetMemberBinder( [NotNull] string name, bool ignoreCase ) : base( name, ignoreCase )
{
}
public override DynamicMetaObject FallbackGetMember( DynamicMetaObject target, DynamicMetaObject errorSuggestion )
{
throw new System.NotImplementedException( );
}
}
Microsoft has very limited documentation on the FallbackGetMember( ) method that can be found here. It appears to me that if you either know that the dynamic object in question will successfully provide a result, or you can wrap your attempt in a try/catch block in order to handle cases where it does not, then the FallbackGetMember( ) need not be implemented. If, however, you require full dynamic dispatch support, then Aaronaught's answer and Dynamity might be the more advisable solution--that simply is not what the question asks though.
In my case, I have a dynamic object that never fails to produce a successful result when TryGetMember( ) is called, so the overhead of true dynamic dispatch seems unreasonably expensive.
精彩评论