49 votes

Inconvénients de Lazy<T> ?

J'ai récemment commencé à utiliser Paresseux dans toute mon application, et je me demandais s'il y avait des aspects négatifs évidents dont je devais tenir compte en utilisant Lazy<T> ?

J'essaie d'utiliser Lazy<T> aussi souvent que je le juge approprié, principalement pour aider à réduire l'empreinte mémoire de nos plugins chargés, mais inactifs.

4 votes

Je viens de commencer à utiliser Lazy<T>, et je trouve que c'est souvent le signe d'une mauvaise conception, ou de la paresse du programmeur. De plus, l'un des inconvénients est qu'il faut être plus vigilant avec les variables de type "scoped up" et créer des fermetures appropriées.

4 votes

@Gleno En quoi consiste exactement la paresse du programmeur ?

4 votes

@Gleno, Anton : Et plus important encore, pourquoi est-ce mauvais ? J'enseigne toujours dans mes cours de programmation que la paresse est une vertu importante chez les programmeurs.

19voto

Gleno Points 4580

Je vais m'étendre un peu sur mon commentaire, qui se lit comme suit :

Je viens de commencer à utiliser Lazy, et je trouve que c'est souvent une indication d'une mauvaise conception ou de la paresse de la part du programmeur. De plus, une inconvénient, c'est qu'il faut être plus vigilant avec les scoped up. et créer des fermetures appropriées.

Par exemple, j'ai utilisé Lazy<T> pour créer les pages que l'utilisateur peut voir dans mon ( sans session ) Application MVC. Il s'agit d'un assistant de guidage, de sorte que l'utilisateur peut vouloir aller à un endroit aléatoire de l'application. précédent étape. Lorsque la poignée de main est effectuée, un tableau de Lazy<Page> est créé, et si l'utilisateur spécifie une étape, cette page exacte est évaluée. Je trouve qu'il offre de bonnes performances, mais il y a certains aspects que je n'aime pas, par exemple beaucoup de mes objets de type foreach ressemble maintenant à ceci :

foreach(var something in somethings){
     var somethingClosure = something;
     list.Add(new Lazy<Page>(() => new Page(somethingClosure));
} 

En d'autres termes, vous devez traiter le problème des fermetures de manière très proactive. Sinon, je ne pense pas que le fait de stocker une lambda et de l'évaluer en cas de besoin soit une si mauvaise performance.

D'un autre côté, cela peut indiquer que le programmeur est un Lazy<Programmer> dans le sens où vous préférez ne pas penser à votre programme maintenant, et laisser la logique appropriée s'évaluer quand c'est nécessaire, comme par exemple dans mon cas - au lieu de construire ce tableau, je pourrais juste déterminer quelle serait la page spécifique demandée ; mais j'ai choisi d'être paresseux, et de faire une approche tout en un.

EDIT

Il me semble que Lazy<T> a également quelques particularités lorsqu'il travaille avec la concurrence. Par exemple, il existe un ThreadLocal<T> pour certains scénarios, et plusieurs configurations de drapeaux pour votre scénario multithread particulier. Vous pouvez en savoir plus sur msdn .

3 votes

Ce n'est pas un problème de Lazy<T> en soi. C'est plutôt la façon dont vous l'utilisez qui compte.

3 votes

@Anton, oui ; et ma conjecture est que Lazy<> est parfois problématique en vous donnant la possibilité de ne pas chercher une meilleure solution. Dans ce cas, vous pourriez vous contenter de quelque chose qui fonctionne tout simplement.

0 votes

@Fuji, disons-le ainsi - le pire qui puisse arriver est que vous deviez soudainement évaluer tous vos objets Lazy en raison d'un changement de spécification, ou faire face à une réécriture majeure. Pouvez-vous vivre avec cela ?

10voto

CSharper Points 416

À mon avis, vous devriez toujours avoir une raison de choisir la paresse. Il existe plusieurs alternatives en fonction du cas d'utilisation et il y a certainement des cas où cette structure est appropriée. Mais ne l'utilisez pas simplement parce que c'est cool.

Par exemple, je ne comprends pas le but de l'exemple de sélection de page dans l'une des autres réponses. L'utilisation d'une liste de Lazy pour sélectionner un seul élément peut très bien se faire avec une liste ou un dictionnaire de délégués directement sans utiliser Lazy ou avec une simple instruction switch.

Les alternatives les plus évidentes sont donc

  • instanciation directe pour les structures de données bon marché ou les structures qui sont de toute façon nécessaires
  • des délégués pour les choses qui sont nécessaires zéro à quelques fois dans un certain algorithme
  • une structure de mise en cache pour les éléments qui doivent libérer la mémoire lorsqu'ils ne sont pas utilisés pendant un certain temps.
  • une sorte de structure "future" comme Task qui peut déjà commencer à s'initialiser de manière asynchrone avant l'utilisation réelle en consommant du temps CPU inactif dans les cas où la probabilité est assez élevée que la structure sera requise plus tard

En revanche, Lazy est souvent adapté lorsque

  • structures de données à forte intensité de calcul
  • sont nécessaires de zéro à plusieurs fois dans un algorithme où le cas zéro a une probabilité significative.
  • les données sont locales à une méthode ou à une classe et peuvent être récupérées lorsqu'elles ne sont plus utilisées ou les données doivent être conservées en mémoire pendant toute la durée d'exécution du programme.

7voto

Thilak Nathen Points 1190

Ce n'est pas vraiment un aspect négatif, mais un piège pour les paresseux :).

Les initialisateurs paresseux sont comme les initialisateurs statiques. Ils sont exécutés une fois . Si une exception est levée, elle est mise en cache et les appels ultérieurs à .Value déclencheront la même exception. Cela est prévu et est mentionné dans la documentation ... http://msdn.microsoft.com/en-us/library/dd642329.aspx :

Les exceptions lancées par valueFactory sont mises en cache.

Par conséquent, le code ci-dessous ne retournera jamais une valeur :

bool firstTime = true;
Lazy<int> lazyInt = new Lazy<int>(() =>
{
    if (firstTime)
    {
        firstTime = false;
        throw new Exception("Always throws exception the very first time.");
    }

    return 21;
});

int? val = null;
while (val == null)
{
    try
    {
        val = lazyInt.Value;
    }
    catch
    {

    }
}

0 votes

Merci @Thilak. C'est intéressant. Je ne savais pas que les exceptions étaient mises en cache de cette manière.

1 votes

@Fuji C'est la raison pour laquelle l'équipe MEF a ajouté ExportFactory et ExportLifetimeContext dans MEF 2. Consultez le site suivant blogs.msdn.com/b/bclteam/archive/2011/11/17/

0 votes

Je pense que je dois revenir en arrière et réviser une partie de mon code MEF maintenant. ;)

6voto

Max Yakimets Points 877

J'en suis venu à utiliser Lazy<T> principalement en raison de ses capacités de concurrence lors du chargement des ressources depuis la base de données. Je me suis donc débarrassé des objets de verrouillage et des modèles de verrouillage discutables. Dans mon cas ConcurrentDictionary + Lazy en tant que valeur a fait ma journée, grâce à @Reed Copsey et son article de blog

Cela ressemble à ce qui suit. Au lieu d'appeler :

MyValue value = dictionary.GetOrAdd(
                             key, 
                             () => new MyValue(key));

Nous utiliserions plutôt un ConcurrentDictionary>, et écrire :

MyValue value = dictionary.GetOrAdd(
                             key, 
                             () => new Lazy<MyValue>(
                                 () => new MyValue(key)))
                          .Value;

Aucun inconvénient de Lazy<T> remarqué jusqu'à présent.

0 votes

C'est plutôt sympa. J'ai malheureusement été un peu trop actif aujourd'hui et j'ai manqué de votes, donc je ne peux pas augmenter le nombre de votes pour votre réponse.

4voto

Anton Gogolev Points 59794

Comme pour tout, Lazy<T> peut être utilisé pour le bien ou pour le mal, d'où un inconvénient : lorsqu'il est utilisé de manière inappropriée, il peut être source de confusion et de frustration. Cependant, le modèle d'initialisation paresseuse existe depuis des années, et maintenant que la BCL de .NET dispose d'une implémentation, les développeurs n'ont pas besoin de réinventer la roue une fois de plus. Qui plus est, Le MEF aime les paresseux .

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