113 votes

C# Propriétés automatiques à chargement paresseux

En C#,

Existe-t-il un moyen de transformer une propriété automatique en une propriété automatique chargée paresseusement avec une valeur par défaut spécifiée ?

Essentiellement, j'essaie de transformer ce...

private string _SomeVariable

public string SomeVariable
{
     get
     {
          if(_SomeVariable == null)
          {
             _SomeVariable = SomeClass.IOnlyWantToCallYouOnce();
          }

          return _SomeVariable;
     }
}

en quelque chose de différent, où je peux spécifier le défaut et il gère le reste automatiquement...

[SetUsing(SomeClass.IOnlyWantToCallYouOnce())]
public string SomeVariable {get; private set;}

0 votes

@Gabe : Notez que la classe ne sera appelée qu'une seule fois si elle ne retourne jamais null.

0 votes

J'ai découvert ça... il semble qu'il utilise le modèle singleton.

123voto

JaredPar Points 333733

Non, il n'y en a pas. Les propriétés auto-implémentées ne servent qu'à implémenter les propriétés les plus basiques : champ d'appui avec getter et setter. Elles ne prennent pas en charge ce type de personnalisation.

Cependant, vous pouvez utiliser la version 4.0 Lazy<T> pour créer ce modèle

private Lazy<string> _someVariable =new Lazy<string>(SomeClass.IOnlyWantToCallYouOnce);
public string SomeVariable => _someVariable.Value;

Ce code va calculer paresseusement la valeur de _someVariable la première fois que le Value est appelée. Elle ne sera calculée qu'une seule fois et mettra la valeur en mémoire cache pour les utilisations futures de l'expression Value propriété

1 votes

En fait, il me semble que Lazy implémente le modèle singleton. Ce n'est pas mon objectif... mon objectif est de créer une propriété chargée paresseusement qui est instanciée paresseusement mais éliminée avec l'instance de la classe dans laquelle elle se trouve. Lazy ne semble pas fonctionner de cette façon.

21 votes

@ctorx Lazy n'a rien à voir avec le modèle singleton. Il fait exactement ce que vous voulez qu'il fasse.

11 votes

Note, SomeClass.IOnlyWantToCallYouOnce dans votre exemple doit être statique pour être utilisé avec un initialisateur de champ.

43voto

Gabe Moothart Points 12400

La solution la plus concise est probablement d'utiliser l'opérateur de coalescence nul :

get { return _SomeVariable ?? (_SomeVariable = SomeClass.IOnlyWantToCallYouOnce()); }

12 votes

Dans l'affaire IOnlyWantToCallYouOnce renvoie à null il l'appellera plus d'une fois.

9 votes

Si vous utilisez l'opérateur de coalescence nul, l'exemple ci-dessus échouera. La syntaxe correcte est la suivante : _SomeVariable ?? ( _SomeVariable = SomeClass.IOnlyWantToCallYouOnce() ); - remarquez l'ajout de la parenthèse autour du réglage _SomeVariable si elle est nulle.

0 votes

C'est la meilleure option. J'ai d'abord utilisé Lazy<> mais pour nos besoins, cela a mieux fonctionné. Avec la dernière version de C#, il est également possible d'écrire de manière encore plus concise => _SomeVariable ?? (_SomeVariable = SomeClass.IOnlyWantToCallYouOnce()); Ce que certains pourraient ne pas remarquer au premier coup d'œil, c'est que l'opérateur évalue l'opérande de droite et renvoie son résultat .

6voto

Aren Points 17843

En revanche, les paramètres des attributs doivent avoir une valeur constante, vous ne pouvez pas appeler de code (même statique).

Il est cependant possible d'implémenter quelque chose avec les aspects de PostSharp.

Regardez-les :

PostSharp

5voto

deepee1 Points 4449

Voici la solution que je propose pour résoudre votre problème. L'idée de base est une propriété qui sera définie par une fonction lors du premier accès et les accès suivants donneront la même valeur de retour que le premier.

public class LazyProperty<T>
{
    bool _initialized = false;
    T _result;

    public T Value(Func<T> fn)
    {
        if (!_initialized)
        {
            _result = fn();
            _initialized = true;
        }
        return _result;
    }
 }

Puis à utiliser :

LazyProperty<Color> _eyeColor = new LazyProperty<Color>();
public Color EyeColor
{ 
    get 
    {
        return _eyeColor.Value(() => SomeCPUHungryMethod());
    } 
}

Il y a bien sûr la surcharge liée au passage du pointeur de fonction, mais cela fait l'affaire pour moi et je ne remarque pas trop de surcharge par rapport à l'exécution répétée de la méthode.

0 votes

Ne serait-il pas plus logique de donner la fonction au constructeur ? Ainsi, vous n'auriez pas à la créer en ligne à chaque fois, et vous pourriez la disposer après la première utilisation.

0 votes

@lund.mikkel oui, cela fonctionnerait aussi. Il peut y avoir des cas d'utilisation pour les deux approches.

5 votes

Si vous passez la fonction au constructeur, un peu comme la classe Lazy de .Net, alors la fonction passée devra être statique, je sais que cela ne correspond pas à ma conception dans de nombreux cas.

2voto

CodesInChaos Points 60274

Je ne pense pas que cela soit possible en C# pur. Mais vous pourriez le faire en utilisant un réécrivain IL comme PostSharp . Par exemple, il vous permet d'ajouter des gestionnaires avant et après les fonctions en fonction des attributs.

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