web-dev-qa-db-ja.com

キャッシュされたプロパティとLazy <T>

.NET 4では、キャッシュされたプロパティを持つ次のスニペットは、 System.Lazy<T> クラス。私は両方のアプローチのパフォーマンスを測定しましたが、それはほとんど同じです。なぜ一方を他方の上に使用する必要があるのか​​について、本当の利点や魔法はありますか?

キャッシュされたプロパティ

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 <T>

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; }
    }
}
50
Martin Buberl

私は一般的にLazy<T>を使用します:

  • スレッドセーフです(この場合は問題にならないかもしれませんが、他の場合は問題になります)
  • 名前だけで何が起こっているのかが明らかになります
  • Nullを有効な値にすることができます

デリゲートにラムダ式を使用しないことに注意してくださいhave。たとえば、これは少しクリーンなアプローチです。

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; }
    }
}

これは、作成プロセスがループなどで複雑になる場合に特に便利です。その外観から、作成コードでGradientStopsのコレクション初期化子を使用できることに注意してください。

別のオプションはnotもちろんこれを怠惰に行うことです...クラスにそのようなプロパティがいくつかあり、関連するオブジェクトを1つずつ作成するだけの場合を除いて、多くの状況でレイジークラスの初期化に依存します。

DoubleDownの回答に記載されているように、これをリセットして再計算を強制する方法はありません(Lazy<T>フィールドを読み取り専用にしない限り)-しかし、それが重要であるとはめったに見つかりませんでした。

71
Jon Skeet

使用する Lazy<T>、それはあなたがしていることを正確に表現しているので-遅延読み込み。

さらに、それはあなたの財産を非常にきれいに保ち、スレッドセーフです。

7
Oded

通常、レイジーを使用しない唯一の理由は、変数をnullにリセットして、次のアクセスで変数が再度ロードされるようにすることです。レイジーにはリセットがないため、レイジーを最初から再作成する必要があります。

4
Double Down

Lazy<T>は、例にスレッドセーフチェックがない場合でも、並行シナリオを正しく処理します(正しい LazyThreadSafetyMode を渡した場合)。

2
marcind

Lazy<T>はより単純で、コードの意図を明確に表現しています。
スレッドセーフでもあります。

これを実際に複数のスレッドで使用している場合は、[ThreadStatic]にする必要があることに注意してください。 GDI +オブジェクトはスレッド間で共有できません。

1
SLaks

Lazyには、スレッドセーフを提供するための同期オーバーヘッドがありますが、キャッシュされたプロパティは、他のコードよりも先にCLRによって初期化され、同期コストを支払う必要はありません。

テスト容易性の観点から、Lazyは十分にテストされ、実証済みのアーティファクトです。

ただし、私の意見では、他のオプションよりもわずかなオーバーヘッドがあります