【问题标题】:Reset System.Lazy重置 System.Lazy
【发布时间】:2011-05-11 08:20:40
【问题描述】:

在商务舱我有:

 class Employee{

      public Employee() {
          m_Manager = new Lazy<Manager>( () => return myRepository.GetManager(ManagerId); );
      }
      public int ManagerId { get; set;}
      private Lazy<Manager> m_Manager;
      public Manager Manager { 
          get {
               return m_Manager.Value;
          }
      }
 }

这工作正常,只有在访问 Manager 属性时才会调用自定义存储库。 现在,如果 ManagerId 更改,我想“重置”我的 manager 属性。该怎么做?

我能做到:

 class Employee{

      public Employee() {
          m_Manager = new Lazy<Manager>( () => return myRepository.GetManager(ManagerId); );
      }
      private int m_ManagerId;
      public int ManagerId { 
          get { return m_ManagerId;}
          set { 
               if(m_ManagerId != value)
               {
                    m_ManagerId = value;
                    m_Manager = new Lazy<Manager>( () => return myRepository.GetManager(ManagerId); );
               }
          }
      }
      private Lazy<Manager> m_Manager;
      public Manager Manager { 
          get {
               return m_Manager.Value;
          }
      }
 }

有没有更清洁的方法来做到这一点?没有m_Manager.Reset() 之类的吗?

【问题讨论】:

    标签: c#-4.0


    【解决方案1】:

    Lazy&lt;T&gt; 没有定义 Reset() 方法。我认为您实现的内容看起来不错。

    【讨论】:

    • 我同意一般的方法很好,但建议将重复的 Lazy 初始化代码提取到共享方法中。
    【解决方案2】:

    如果您对使用未记录的行为和私有字段感到满意,可以使用以下方法:

    public static void ResetPublicationOnly<T>(this Lazy<T> lazy)
    {
        const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
    
        LazyThreadSafetyMode mode = (LazyThreadSafetyMode)typeof(Lazy<T>).GetProperty("Mode", flags).GetValue(lazy, null);
        if (mode != LazyThreadSafetyMode.PublicationOnly)
            throw new InvalidOperationException("ResetPublicationOnly only works for Lazy<T> with LazyThreadSafetyMode.PublicationOnly");
    
        typeof(Lazy<T>).GetField("m_boxed", flags).SetValue(lazy, null); 
    }
    

    还有一些使用测试:

    Lazy<string> val = new Lazy<string>(() => "hola" + DateTime.Now.Ticks, LazyThreadSafetyMode.PublicationOnly);
    
    val.ResetPublicationOnly(); //reset before initialized
    var str1 = val.Value;
    val.ResetPublicationOnly(); //reset after initialized
    
    var str2 = val.Value;
    
    Assert.AreNotEqual(str1, str2); 
    

    编辑:已弃用!此解决方案不再像 Keith 所指出的那样有效。我们已经在 Signum 框架中构建了自己的 ResetLazy

    public interface IResetLazy
    {
        void Reset();
        void Load();
        Type DeclaringType { get; }
    }
    
    [ComVisible(false)]
    [HostProtection(Action = SecurityAction.LinkDemand, Resources = HostProtectionResource.Synchronization | HostProtectionResource.SharedState)]
    public class ResetLazy<T>: IResetLazy
    {
        class Box
        {
            public Box(T value)
            {
                this.Value = value;
            }
    
            public readonly T Value;
        }
    
        public ResetLazy(Func<T> valueFactory, LazyThreadSafetyMode mode = LazyThreadSafetyMode.PublicationOnly, Type declaringType = null)
        {
            if (valueFactory == null)
                throw new ArgumentNullException("valueFactory");
    
            this.mode = mode;
            this.valueFactory = valueFactory;
            this.declaringType = declaringType ?? valueFactory.Method.DeclaringType;
        }
    
        LazyThreadSafetyMode mode; 
        Func<T> valueFactory;
    
        object syncLock = new object();
    
        Box box;
    
        Type declaringType; 
        public Type DeclaringType
        {
            get { return declaringType; }
        }
    
        public T Value
        {
            get
            {
                var b1 = this.box;
                if (b1 != null)
                    return b1.Value;
    
                if (mode == LazyThreadSafetyMode.ExecutionAndPublication)
                {
                    lock (syncLock)
                    {
                        var b2 = box;
                        if (b2 != null)
                            return b2.Value;
    
                        this.box = new Box(valueFactory());
    
                        return box.Value;
                    }
                }
    
                else if (mode == LazyThreadSafetyMode.PublicationOnly)
                {
                    var newValue = valueFactory(); 
    
                    lock (syncLock)
                    {
                        var b2 = box;
                        if (b2 != null)
                            return b2.Value;
    
                        this.box = new Box(newValue);
    
                        return box.Value;
                    }
                }
                else
                {
                    var b = new Box(valueFactory());
                    this.box = b;
                    return b.Value;
                }
            }
        }
    
    
        public void Load()
        {
            var a = Value;
        }
    
        public bool IsValueCreated
        {
            get { return box != null; }
        }
    
        public void Reset()
        {
            if (mode != LazyThreadSafetyMode.None)
            {
                lock (syncLock)
                {
                    this.box = null;
                }
            }
            else
            {
                this.box = null;
            }
        }
    }
    

    【讨论】:

    • 希望你不要陷入困境
    • 看起来他们对 Lazy 源代码进行了一些更改。此方法不再有效,会抛出异常
    • 一个很好的例子,说明为什么不使用无证行为和私有字段!
    • 根据问题中的代码重新分配变量要好得多...简单可靠,易于线程安全。
    • 我认为同一个线程可以使用同一个锁两次,这就是你的意思,但是与referencesource.microsoft.com/#mscorlib/system/… 相比,ResetLazy 的工作方式存在根本差异,这让我感到紧张。无论如何,我使用它很多没有问题。
    【解决方案3】:

    在重新实例化 Lazy 之前,您还可以添加一个仅在初始化为“if(m_Manager.IsValueCreated)”时重置的条件。

    【讨论】:

    • 这将创建一个竞争条件 - 如果另一个线程已经开始创建值,并且已经读取(原始)ManagerId,并且此时 ManagerId 已更改,那么您会看到 IsValueCreated 为 false ,而不是重新实例化 Lazy。片刻之后,另一个线程成功创建了 Manager 对象,并且所有后续调用者都收到了错误的 manager 对象。使用原始代码,只有在 ManagerId 更改之前至少开始访问 Employee 对象的调用者才能看到旧的 Manager 对象。
    猜你喜欢
    • 1970-01-01
    • 2014-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-04
    • 1970-01-01
    • 2013-08-10
    • 2013-05-02
    相关资源
    最近更新 更多