开发者

Reset System.Lazy

In a business class I have :

 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 {开发者_Python百科
               return m_Manager.Value;
          }
      }
 }

This is working correctly, the custom repository is called only if accessing the Manager property. Now I want to "reset" my manager property if the ManagerId changed. How to do that ?

I can do :

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

Is there a cleaner way to do that ? Isn't there a m_Manager.Reset() or something like this ?


Lazy<T> does not define a Reset() method. What you've implemented looks fine I think.


If you are happy with using undocumented behaviour and private fields, here is a method to do it:

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

And some test for usage:

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

EDIT: Deprecated! This solution no longer works as pointed by Keith. We have build owr own ResetLazy in Signum Framework

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


Before you re-instanciate the Lazy you can also add a condition for reseting only if it was initalized "if(m_Manager.IsValueCreated)".

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜