IDynamicMetaObjectProvider set property using literal name

431 Views Asked by At

I need to set DynamicObject property using string propertyName. I found the way to get property value using this answer, but when it comes to setValue I'm not quite sure how to rewrite the code in order to set the property. I receive runtime errors and not quite sure about expressions logic. I wonder if you can suggest any idea how to implement void SetProperty(object o, string member,object value) method.

1

There are 1 best solutions below

6
On BEST ANSWER

In some cases like ExpandoObject, then you can use the IDictionary<string,object> API instead:

ExpandoObject obj = ...
var dict =  (IDictionary<string, object>)obj;
object oldVal = dict[memberName];
dict[memberName] = newVal;

In the more general case of IDynamicMetaObjectProvider: you could borrow the CallSiteCache from FastMember:

internal static class CallSiteCache
{
    private static readonly Hashtable getters = new Hashtable(), setters = new Hashtable();

    internal static object GetValue(string name, object target)
    {
        CallSite<Func<CallSite, object, object>> callSite = (CallSite<Func<CallSite, object, object>>)getters[name];
        if (callSite == null)
        {
            lock (getters)
            {
                callSite = (CallSite<Func<CallSite, object, object>>)getters[name];
                if (callSite == null)
                {
                    CallSite<Func<CallSite, object, object>> newSite = CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember(CSharpBinderFlags.None, name, typeof(CallSiteCache), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
                    getters[name] = callSite = newSite;
                }
            }
        }
        return callSite.Target(callSite, target);
    }
    internal static void SetValue(string name, object target, object value)
    {
        CallSite<Func<CallSite, object, object, object>> callSite = (CallSite<Func<CallSite, object, object, object>>)setters[name];
        if (callSite == null)
        {
            lock (setters)
            {
                callSite = (CallSite<Func<CallSite, object, object, object>>)setters[name];
                if (callSite == null)
                {
                    CallSite<Func<CallSite, object, object, object>> newSite = CallSite<Func<CallSite, object, object, object>>.Create(Binder.SetMember(CSharpBinderFlags.None, name, typeof(CallSiteCache), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) }));
                    setters[name] = callSite = newSite;
                }
            }
        }
        callSite.Target(callSite, target, value);
    }
}

Note here that we could type target as IDynamicMetaObjectProvider, but we don't actually need that - the CallSite API doesn't require it.