1

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.

Community
  • 1
  • 1
Access Denied
  • 8,723
  • 4
  • 42
  • 72
  • The code you link to uses reflection, not `IDynamicMetaObjectProvider`, surely? – Marc Gravell Jun 10 '15 at 13:20
  • First part of the linked code (where provider!=null) is what I'm interested in. – Access Denied Jun 10 '15 at 13:28
  • yeah, but that's still doing things the hard way... and it doesn't benefit from any caching (plus `Compile` and `DynamicInvoke` each time... yeuch); seriously; the `CallSiteCache` code shown works fine... basically it sub-contracts all this mess to the provider/call-site, and caches the strategy – Marc Gravell Jun 10 '15 at 13:33

1 Answers1

3

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Work pretty good, thanks :) Had to change Hashtable to Dictionary, because I need it in PCL, but it was not a big deal. – Access Denied Jun 11 '15 at 07:05
  • @AccessDenied important note: dictionary has different thread-safety semantics to `Hashtable`; if you have made that swap, you can no longer safely do the first check outside the `lock`; basically: move everything **inside** the `lock`; obviously there would be no point in double-checked locking in that case either – Marc Gravell Jun 11 '15 at 07:10
  • But why can't we apply double check lock with dictionary? – Access Denied Jun 11 '15 at 07:19
  • @AccessDenied there's no point if you are already inside the lock.... the *definition* of double-checked is: to check outside the lock, lock, check inside the lock, then *do the thing*; if you **must** lock, there's no point checking twice inside; it isn't going to change because *you have the lock* – Marc Gravell Jun 11 '15 at 07:23
  • Could you be so kind to take a look at my code snippet and tell me what is wrong with it? Can't get what is wrong with it. http://codeshare.io/J5RpN – Access Denied Jun 11 '15 at 07:35
  • 1
    @AccessDenied simple: it isn't thread-safe; `TryGetValue` can fail if the dictionary is being mutated by a competing thread (meaning: the `TryGetValue` that is *outside* the `lock`) – Marc Gravell Jun 11 '15 at 08:02