Cached property vs Lazy

W. Net 4 poniższy fragment z buforowaną właściwością może być również napisany przy użyciu System.Lazy<T> klasy. Zmierzyłem wydajność obu podejść i jest prawie taka sama. Czy jest jakaś prawdziwa korzyść lub magia, dlaczego powinienem używać jednego nad drugim?

Cached Property

public static class Brushes
{
    private static LinearGradientBrush _myBrush;

    public static LinearGradientBrush MyBrush
    {
        get
        {
            if (_myBrush == null)
            {
                var linearGradientBrush = new LinearGradientBrush { ...};
                linearGradientBrush.GradientStops.Add( ... );
                linearGradientBrush.GradientStops.Add( ... );

                _myBrush = linearGradientBrush;
            }

            return _myBrush;
        }
    }
}

Lazy

public static class Brushes
{
    private static readonly Lazy<LinearGradientBrush> _myBrush =
        new Lazy<LinearGradientBrush>(() =>
            {
                var linearGradientBrush = new LinearGradientBrush { ...};
                linearGradientBrush.GradientStops.Add( ... );
                linearGradientBrush.GradientStops.Add( ... );

                return linearGradientBrush;
            }
        );

    public static LinearGradientBrush MyBrush
    {
        get { return _myBrush.Value; }
    }
}
Author: BoltClock, 2011-02-27

7 answers

Użyłbym Lazy<T> ogólnie:

    Jest bezpieczny dla wątków (może nie być problemem w tym przypadku, ale będzie w innych)]}
  • to sprawia, że oczywiste jest, co się dzieje tylko po nazwie
  • pozwala null być poprawną wartością

Zauważ, że nie musisz używać wyrażenia lambda dla delegata. Na przykład, oto podejście, które może być nieco czystsze:

public static class Brushes
{
    private static readonly Lazy<LinearGradientBrush> _myBrush =
        new Lazy<LinearGradientBrush>(CreateMyBrush);

    private static LinearGradientBrush CreateMyBrush()
    {
        var linearGradientBrush = new LinearGradientBrush { ...};
        linearGradientBrush.GradientStops.Add( ... );
        linearGradientBrush.GradientStops.Add( ... );

        return linearGradientBrush;
    }

    public static LinearGradientBrush MyBrush
    {
        get { return _myBrush.Value; }
    }
}

Jest to szczególnie przydatne, gdy proces tworzenia komplikuje się za pomocą pętli itd. Zauważ, że wygląda na to, że możesz użyć inicjalizatora kolekcji dla GradientStops w kodzie tworzenia.

Inną opcją jest Nie , aby robić to leniwie, oczywiście... jeśli nie masz kilku takich właściwości w swojej klasie i chcesz tworzyć odpowiednie obiekty tylko jeden po drugim, możesz polegać na leniwej inicjalizacji klasy w wielu sytuacjach.

Jak wspomniano w odpowiedzi DoubleDown, nie ma sposobu na zresetowanie tego, aby wymusić rekomputację (chyba, że zrobisz Lazy<T> field not readonly) - ale bardzo rzadko uznałem to za ważne.

 69
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-31 10:28:56

Użyj Lazy<T>, ponieważ wyraża dokładnie to, co robisz-leniwe Ładowanie.

Ponadto utrzymuje Twoją nieruchomość w czystości i jest Bezpieczna.

 7
Author: Oded,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-27 17:49:49

Zazwyczaj jedynym powodem, aby nie używać lazy jest reset zmiennej do wartości null, więc następny dostęp powoduje jej ponowne załadowanie. Lazy nie ma resetu i musisz odtworzyć leniwego od zera.

 4
Author: Double Down,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-27 18:59:15

Lazy<T> będzie poprawnie obsługiwać współbieżne scenariusze (jeśli przekażesz poprawne LazyThreadSafetyMode ), podczas gdy twój przykład nie ma żadnych kontroli bezpieczeństwa wątków.

 2
Author: marcind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-27 17:49:40

{[0] } jest prostsza-jasno wyraża intencję Kodeksu.
Jest również bezpieczny dla nici.

Zauważ, że jeśli używasz tego w wielu wątkach, musisz zrobić to [ThreadStatic]; obiekty GDI+ nie mogą być współdzielone między wątkami.

 1
Author: SLaks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-27 17:49:37

Lazy ma narzut synchronizacji, aby zapewnić bezpieczeństwo wątku, podczas gdy właściwość buforowana jest inicjowana przez CLR przed jakimkolwiek innym kodem i nie musisz płacić kosztów synronizacji

Z punktu widzenia testowalności, Lazy jest dobrze przetestowanym i sprawdzonym artefaktem.

Ma jednak, moim zdaniem, bardzo lekki overhead nad inną opcją

 0
Author: ibrahim durmus - redmond,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-07 09:53:14

Cóż, jeśli Twoja wydajność jest mniej więcej taka sama, to jedynym powodem do użycia Lazy<T> nad buforowaną wersją byłoby to, że nie jesteś pewien, czy użytkownik rzeczywiście załaduje właściwość.

Punktem Lazy<T> jest oczekiwanie, aż użytkownik będzie potrzebował zasobu, a następnie utworzenie go w danej instancji w czasie. Jeśli zawsze będą potrzebować zasobów, nie ma sensu używać Lazy<T>, chyba że potrzebujesz innych celów, takich jak bezpieczeństwo wątku.

 -1
Author: msarchet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-27 17:51:55