【问题标题】:IDynamicMetaObjectProvider set property using literal nameIDynamicMetaObjectProvider 使用文字名称设置属性
【发布时间】:2015-06-11 15:26:17
【问题描述】:

我需要使用字符串 propertyName 设置 DynamicObject 属性。我找到了使用此answer 获取属性值的方法,但是当涉及到 setValue 时,我不太确定如何重写代码以设置属性。我收到运行时错误并且不太确定表达式逻辑。我想知道您是否可以提出任何想法如何实现void SetProperty(object o, string member,object value) 方法。

【问题讨论】:

  • 你链接到的代码使用反射,而不是IDynamicMetaObjectProvider,确定吗?
  • 链接代码的第一部分(provider!=null)是我感兴趣的。
  • 是的,但这仍然是一种艰难的方式......而且它并没有从任何缓存中受益(加上CompileDynamicInvoke每次...... yeuch);严重地;显示的CallSiteCache 代码工作正常......基本上它将所有这些混乱分包给提供者/调用站点,并缓存策略

标签: c# dynamic


【解决方案1】:

在某些情况下,例如ExpandoObject,您可以改用IDictionary<string,object> API:

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

在更一般的IDynamicMetaObjectProvider 情况下:您可以从FastMember 借用CallSiteCache

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);
    }
}

请注意,我们可以target 输入为IDynamicMetaObjectProvider,但我们实际上并不需要它 - CallSite API 不需要它。

【讨论】:

  • 工作得很好,谢谢 :) 不得不将 Hashtable 更改为 Dictionary,因为我在 PCL 中需要它,但这没什么大不了的。
  • @AccessDenied 重要说明:字典与Hashtable 具有不同的线程安全语义;如果您进行了该交换,则无法再安全地在lock 之外进行第一次检查;基本上:将lock内部的所有内容移动;显然,在这种情况下,双重检查锁定也没有意义
  • 但是为什么我们不能用字典应用双重检查锁呢?
  • @AccessDenied 如果你已经在锁里面就没意义了....双重检查的定义是:检查锁外,锁,检查内锁定,然后做事;如果您必须锁定,那么在里面检查两次是没有意义的;它不会改变,因为你有锁
  • @AccessDenied 很简单:它不是线程安全的;如果字典被竞争线程改变,TryGetValue 可能会失败(意思是:TryGetValue lock 之外
猜你喜欢
  • 1970-01-01
  • 2012-03-13
  • 2014-02-08
  • 2021-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-24
  • 1970-01-01
相关资源
最近更新 更多