Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
449 views
in Technique[技术] by (71.8m points)

.net - How to call DynamicObject.TryGetMember directly?

I'm implementing a general purpose function to extract a value from an arbitrary provided dynamic object, but 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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...