Je suis tombé sur ce morceau de code où il semble que le développeur d'origine ait essayé d'utiliser une chaîne statique pour mettre en cache une valeur dans une classe statique.

public static class GetStringFromSomeProcess
{
    private static string theAnswer;

    public static string GetString
    {
        get
        {
            if(theAnswer == null)
            {
                theAnswer = GoGetTheAnswerFromALongRunningProcess();
            }
            return theAnswer;
        }
    }
}   

Pour autant que je sache, cela ne fonctionnera pas, car vous ne pouvez pas instancier la classe GetStringFromSomeProcess, GoGetTheAnswerFromALongRunningProcess sera appelé à chaque fois que GetString est utilisé. Est-ce que je manque quelque chose?

4
jonaglon 21 avril 2017 à 12:42

3 réponses

Meilleure réponse

Vous avez raison de dire que la classe ne peut pas être instanciée, mais que la classe existera dans l'application.

Par conséquent, la méthode GetStringFromSomeProcess ne sera appelée que lors du premier accès à la propriété. Toutes les deux fois par la suite, la vérification de == null se résoudra en false et la valeur évaluée par le premier appel sera renvoyée.

4
Luke 21 avril 2017 à 09:45

Fonctionne-t-il correctement sans créer un objet de la classe GetStringFromSomeProces? Étant donné que la chaîne, theAnswer, est également statique, cela pourrait potentiellement fonctionner, mais je me demande quand cette variable sera initialisée. En règle générale, vous le codez comme vous le suggérez avec l'initialisation de la classe GetStringFromSomeProcess.

Main.cs

...
GetStringFromSomeProcess getString = new GetStringFromSomeProcess();
string answer = getString.theAnswer();
...

GetStringFromSomeProcess.cs

public class GetStringFromSomeProcess
{
    private string _theAnswer;

    public string theAnswer
    {
        get
        {
            if(theAnswer == null)
            {
                GoGetTheAnswerFromALongRunningProcess getAnswer = new GoGetTheAnswerFromALongRunningProcess();
                _theAnswer = getAnswer.GetAnswer();
            }
            return _theAnswer;
        }
    }
}
0
Huda Syed 21 avril 2017 à 09:58

Cela fonctionnera très bien - il n'y a qu'une seule instance de theAnswer car elle est statique - et (également parce qu'elle est statique) elle est accessible à partir d'une propriété statique publique. Cela signifie que toutes les modifications qui y sont apportées seront visibles par tout le code qui y accède. Ainsi, le premier appel à GetString définira theAnswer sur non nul, et les appels suivants ne feront pas d'appel à GetStringFromSomeProcess().

Cependant, la solution que vous avez publiée n'est pas threadsafe car GoGetTheAnswerFromALongRunningProcess() peut être appelée simultanément par plusieurs threads.

.Net fournit le Lazy pour résoudre ce problème, comme suit:

public static class GetStringFromSomeProcess
{
    private static readonly Lazy<string> _theAnswer = new Lazy<string>(GoGetTheAnswerFromALongRunningProcess);

    public static string GetString
    {
        get
        {
            return _theAnswer.Value;
        }
    }

    public static string GoGetTheAnswerFromALongRunningProcess()
    {
        return "X";
    }
}

Vous fournissez au constructeur de la classe Lazy<T> une méthode qu'il peut appeler en cas de besoin afin de créer l'objet qu'il encapsule. Dans l'exemple ci-dessus, je passe GoGetTheAnswerFromALongRunningProcess à son constructeur.

Notez également que c'est généralement une mauvaise idée d'avoir une propriété dont le retour peut prendre beaucoup de temps. Il vaut mieux en faire une méthode:

public static string GetString()
{
    return _theAnswer.Value;
}
4
Matthew Watson 21 avril 2017 à 09:58