2011-05-11 4 views
28

En una clase de negocios que tengo:Restablecer System.Lazy

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

Esto está funcionando correctamente, el repositorio personalizado se llama sólo si el acceso a la propiedad Manager. Ahora quiero "restablecer" la propiedad de mi administrador si el ManagerId cambió. Como hacer eso ?

que puedo hacer:

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

¿Hay una forma más limpia de hacer eso? ¿No hay un m_Manager.Reset() o algo como esto?

Respuesta

15

Lazy<T> no define un método Reset(). Lo que has implementado se ve bien, creo.

8

Si está satisfecho con el uso de un comportamiento no documentado y campos privados, que aquí es un método para hacerlo:

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

Y alguna prueba para el uso:

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! Esta solución ya no funciona como señaló Keith. Tenemos la construcción propia de ResetLazy en 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; 
     } 
    } 
} 
+0

un poco complicado, pero como se puede encapsular en un método de extensión, esto podría simplificar mucho mi código. thx –

+1

espero que no esté en un círculo cerrado –

+2

parece que han realizado algunos cambios en el código fuente Lazy. Este método ya no funciona y se lanzará una excepción – Keith