9 votes

Quand faire une charge paresseuse ?

Je charge paresseusement tous mes membres. Je fais cela depuis un certain temps et j'ai simplement considéré que le chargement paresseux était une bonne chose au premier abord.

Disons que nous avons

public class SomeClass
{
   public int anInt;
   public SomeReferenceType member1;

   public SomeClass()
   {
      //initialize members in constructor when needed (lazy load)
      anInt = new int();
      member1 = new SomeReferenceType();
   }
}

Y a-t-il des inconvénients à faire les choses de cette manière ? Est-ce un bon modèle de chargement paresseux ? Cela a-t-il un sens de charger paresseusement un type de valeur (avec la RAM moderne, est-ce même important) ?


Après ce que j'ai appris de vos réponses, j'aimerais savoir s'il y a une différence entre ce qui précède et ceci...

public class SomeClass
    {
       public int anInt;
       public SomeReferenceType member1 = new SomeReferenceType();

       public SomeClass()
       {

       }
    }

18voto

Justin Niessner Points 144953

Tout d'abord, initialiser un membre dans le constructeur n'est pas un chargement paresseux.

Le chargement paresseux consiste à initialiser le membre la première fois qu'il est demandé. Un exemple simple en .NET (avec une double vérification du verrouillage pour éviter les problèmes de threading) :

public class SomeClass
{
    private object _lockObj = new object();
    private SomeReferenceType _someProperty;

    public SomeReferenceType SomeProperty
    {
        get
        {
            if(_someProperty== null)
            {
                lock(_lockObj)
                {
                    if(_someProperty== null)
                    {
                        _someProperty= new SomeReferenceType();
                    }
                }
            }
            return _someProperty;
        }
        set { _someProperty = value; }
    }
}

Heureusement, si vous utilisez .NET 4, vous pouvez désormais utiliser la fonction Lazy<T> Classe qui s'occupe des problèmes pour vous et vous facilite grandement la tâche.

Ensuite, le chargement paresseux est une bonne idée lorsque vous avez de nombreux membres dont le chargement pourrait être coûteux et que vous êtes sûr d'utiliser toutes ces valeurs. Ce coût rendrait l'instanciation du type inutilement lente.

Le chargement paresseux pour le simple plaisir de charger paresseusement ajoute une complexité inutile à votre code et peut causer des problèmes à l'avenir s'il n'est pas fait correctement (lorsqu'il s'agit de threading, par exemple).

8voto

James Michael Hare Points 19077

Ce n'est pas vraiment une charge paresseuse. C'est une initialisation à la construction. Typiquement, ce que nous entendons par chargement paresseux est de construire l'élément la première fois qu'il est référencé.

    private string _someField;

    public string SomeField
    {
        get 
        {
            // we'd also want to do synchronization if multi-threading.
            if (_someField == null)
            {
                _someField = new String('-', 1000000);
            }

            return _someField;
        }
    }

Auparavant, l'une des méthodes typiques de chargement paresseux était un check,lock,check afin de ne pas verrouiller s'il a déjà été créé, mais comme il est possible que deux éléments passent le check et attendent le lock, vous vérifiez à nouveau dans le lock :

public class SomeClass
{
    private string _someField;

    private readonly object _lazyLock = new object();

    public string SomeField
    {
        get 
        {
            // we'd also want to do synchronization if multi-threading.
            if (_someField == null)
            {
                lock (_lazyLock)
                {
                    if (_someField == null)
                    {
                        _someField = new String('-', 1000000);
                    }
                }
            }

            return _someField;
        }
    }
}

Il existe plusieurs façons de procéder, en fait, dans .NET 4.0, il existe une fonction Lazy<T> qui peut vous aider à effectuer facilement un chargement paresseux à l'abri des fils.

public class SomeClass
{
    private readonly Lazy<string> _someField = new Lazy<string>(() => new string('-', 10000000), true);

    private readonly object _lazyLock = new object();

    public string SomeField
    {
        get
        {
            return _someField.Value;
        }
    }
}

Pour ce qui est du pourquoi, le chargement paresseux est généralement une bonne solution si l'objet que vous créez a tendance à être coûteux (en mémoire ou en temps) et s'il n'y a aucune garantie que vous en ayez besoin. Si vous êtes raisonnablement sûr qu'il sera toujours utilisé, alors vous devriez le construire purement et simplement.

6voto

flq Points 11937

D'après le code que je vois, vous ne faites pas de chargement paresseux. Vous initialisez les membres dans le constructeur, ce qui arrive toujours et très tôt dans la vie de l'instance.

C'est pourquoi je me demande ce qu'est pour vous une charge non-lazy.

Le chargement paresseux consiste généralement à initialiser quelque chose uniquement lorsque vous y accédez.

Voici un exemple, utilisant la classe .NET 4.0 Lazy, qui vous aide à faire exactement cela, le chargement paresseux :

public class Foo
{
    private Lazy<int> _value = new Lazy<int>(() => 3);

    public int Value { get { return _value.Value; } }
}

En ce qui concerne le thread-safety - vous pouvez passer un deuxième argument LazyThreadSafetyMode qui connaît deux façons de spécifier la sécurité des fils : Une dans laquelle l'exécution de la méthode d'initialisation peut se produire plusieurs fois, mais où tous les threads obtiennent la valeur qui a été créée en premier, ou une dans laquelle l'exécution est également protégée contre le fait d'être exécutée plusieurs fois.

0voto

fix_likes_coding Points 3586

Ce n'est pas un chargement paresseux.

le chargement paresseux signifierait que vous ne chargez la valeur qu'au moment d'un accès réel (ce qui n'arrive pas dans l'initialisateur).

le chargement paresseux est quelque chose comme ça :

private SomeClass _someRef = null;
public SomeClass SomeRef
{
  get
  {
    if(_someRef == null)
    {
       //initialisation just in case of access
       _someRef = new  SomeClass();
    }
    return _someRef;
  }
}

0voto

Timwi Points 30896

Une véritable propriété paresseuse pour un int pourrait ressembler à quelque chose comme ceci :

private int? _heavyLoadedInt;

public int HeavyLoading
{
    get
    {
        if (_heavyLoadedInt == null)
            _heavyLoadedInt = DoHeavyLoading();
        return _heavyLoadedInt.Value;
    }
}

Maintenant, si vous regardez ça, vous verrez qu'il y a des frais généraux ici : Vous devez stocker la valeur dans un nullable (mémoire supplémentaire), vérifier qu'il n'y a pas d'erreur. null à chaque accès, et récupère la valeur du nullable à chaque accès.

Si votre entier nécessite vraiment des calculs très lourds, cette construction a du sens. Mais new int() n'est pas un calcul lourd, il retourne simplement 0 . La surcharge est minuscule, mais si vous ajoutez cette surcharge à une opération encore plus minuscule (qui consiste à lire un entier), cela n'a aucun sens.

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