46 votes

Réinitialiser System.Lazy

Dans une classe affaires, j'ai :

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

Cela fonctionne correctement, le dépôt personnalisé est appelé uniquement si vous accédez à la propriété Manager. Maintenant, je veux "réinitialiser" la propriété de mon manager si le ManagerId a changé. Comment faire ?

Je peux faire :

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

Y a-t-il une façon plus propre de le faire ? N'y a-t-il pas un m_Manager.Reset() ou quelque chose comme ça ?

22voto

Nathan Points 3564

Lazy<T> ne définit pas de méthode Reset(). Ce que vous avez mis en œuvre semble bien, je pense.

10voto

Olmo Points 1928

Si vous êtes satisfait d'utiliser un comportement non documenté et des champs privés, voici une méthode pour le faire :

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

Et quelques tests d'utilisation :

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

ÉDITION : Obsolète ! Cette solution ne fonctionne plus comme indiqué par Keith. Nous avons construit sa propre ResetLazy dans 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;
        }
    }
}

0voto

remi bourgarel Points 3893

Avant de rétablir le Paresseux, vous pouvez également ajouter une condition de réinitialisation uniquement si elle a été initialisée "if(m_Manager.IsValueCreated)".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X