3

私は最近 .Net の Lazy クラスについて学びましたが、おそらくそれを使いすぎています。以下の例では、熱心な方法で評価できた可能性がありますが、何度も呼び出されると同じ計算が繰り返されることになります。この特定の例では、Lazy を使用するコストが利点を正当化しない可能性があります。ラムダと遅延呼び出しがどれほど高価であるかをまだ理解していないため、これについてはわかりません。複雑なロジックを小さな管理しやすいチャンクに分割できるため、チェーン化された Lazy プロパティを使用するのが好きです。また、何かを初期化するのに最適な場所を考える必要もなくなりました。知っておく必要があるのは、使用しない場合は初期化されず、使用を開始する前に一度だけ初期化されるということだけです。しかし、lazy とラムダを使い始めると、単純なクラスがより複雑になりました。これが正当化される時期と、これが複雑さ、読みやすさ、おそらく速度の点でやり過ぎである時期を客観的に判断することはできません。あなたの一般的な推奨事項は何ですか?

    // This is set once during initialization.
    // The other 3 properties are derived from this one.
    // Ends in .dat
    public string DatFileName
    {
        get;
        private set;
    }

    private Lazy<string> DatFileBase
    {
        get
        {
            // Removes .dat
            return new Lazy<string>(() => Path.GetFileNameWithoutExtension(this.DatFileName));
        }
    }

    public Lazy<string> MicrosoftFormatName
    {
        get
        {
            return new Lazy<string>(() => this.DatFileBase + "_m.fmt");
        }
    }

    public Lazy<string> OracleFormatName
    {
        get
        {
            return new Lazy<string>(() => this.DatFileBase + "_o.fmt");
        }
    }
4

5 に答える 5

2

「初期化するのに最適な場所はどこか考える必要がなくなった」とおっしゃいました

これは悪い習慣です。プログラムで何が起こっているかを正確に知る必要があります

渡す必要があるが計算が必要なオブジェクトがある場合は、 Lazy<>を使用する必要があります。したがって、それが使用される場合にのみ計算されます。

それに加えて、レイジーで取得するオブジェクトは、要求されたときにプログラムの状態にあったオブジェクトではないことを覚えておく必要があります。
オブジェクト自体は、使用される場合にのみ取得されます。プログラムの状態にとって重要なオブジェクトを取得すると、後でデバッグするのが難しくなります。

于 2011-10-07T17:06:12.240 に答える
2

これはおそらく少しやり過ぎです。

Lazy は通常、ジェネリック型を作成または評価するのにコストがかかる場合、および/または依存クラスのすべての使用でジェネリック型が常に必要であるとは限らない場合に使用する必要があります。

多くの場合、ここでゲッターを呼び出すものには、ゲッターを呼び出した直後に実際の文字列値が必要になります。このような場合に Lazy を返す必要はありません。呼び出し元のコードは、Lazy インスタンスをただちに評価して、本当に必要なものを取得するだけだからです。Lazy の「ジャスト イン タイム」の性質がここでは無駄になっているため、YAGNI (You Ain't Gonna Need It) になります。

とはいえ、Lazy に固有の「オーバーヘッド」はそれほど大きくありません。Lazy は、ジェネリック型を生成するラムダを参照するクラスにすぎません。ラムダは定義と実行が比較的安価です。これらは単なるメソッドであり、コンパイル時に CLR によってマッシュアップ名が付けられます。余分なクラスのインスタンス化が主なキッカーですが、それでもひどいものではありません。ただし、コーディングとパフォーマンスの両方の観点から、これは不要なオーバーヘッドです。

于 2011-10-07T17:08:13.167 に答える
1

これはLazy<T>、実行の遅延のために任意のデリゲートをラップするために(おそらく意図せずに)高価なオブジェクトの作成/ロードを節約する目的で使用されているようには見えません。派生プロパティゲッターが返すことをおそらく望んでいる/意図しているのは、オブジェクトstringではなく、です。Lazy<string>

呼び出し元のコードが次のようになっている場合

string fileName = MicrosoftFormatName.Value;

すぐに「遅延読み込み」を行うので、明らかに意味がありません。

呼び出し元のコードが次のようになっている場合

var lazyName = MicrosoftFormatName; // Not yet evaluated
// some other stuff, maybe changing the value of DatFileName
string fileName2 = lazyName.Value;

そうすれば、オブジェクトが作成されfileName2たときに判別できない可能性があることがわかります。lazyName

Lazy<T>公共の財産に最適に使用されていないように私には思えます。ここで、ゲッターは新しい(真新しい、別個の、余分な)Lazy<string>オブジェクトを返しているので、各呼び出し元は(潜在的に)異なる .Valueオブジェクトを取得します!すべてのプロパティは、最初にアクセスしたときに設定されていることLazy<string>に依存しているため、派生した各プロパティの使用に関連して、いつ初期化されるか常に考慮する必要があります。DatFileName.Value

次のようなプライベートバッキング変数とパブリックプロパティゲッターを作成するMSDNの記事「LazyInitialization 」を参照してください。Lazy<T>

get { return _privateLazyObject.Value; }

Lazy<string>「set-once」基本プロパティを定義するために使用して、コードがどのようにすべきか、またはどのようになるかを推測します。

// This is set up once (durinig object initialization) and
// evaluated once (the first time _datFileName.Value is accessed)
private Lazy<string> _datFileName = new Lazy<string>(() =>
    {
        string filename = null;
        //Insert initialization code here to determine filename
        return filename;
    });

// The other 3 properties are derived from this one.
// Ends in .dat
public string DatFileName
{
    get { return _datFileName.Value; }
    private set { _datFileName = new Lazy<string>(() => value); }
}

private string DatFileBase
{
    get { return Path.GetFileNameWithoutExtension(DatFileName); }
}

public string MicrosoftFormatName
{
    get { return DatFileBase + "_m.fmt"; }
}

public string OracleFormatName
{
    get { return DatFileBase + "_o.fmt"; }
}
于 2012-07-15T16:17:26.077 に答える
0

Lazy単純な文字列プロパティを作成するために使用するのは、確かにやり過ぎです。ラムダ パラメーターを使用して のインスタンスを初期化すると、Lazy単一の文字列操作を実行するよりもはるかにコストがかかる可能性があります。他の人がまだ言及していない重要な引数がもう 1 つあります。ラムダ パラメータは、コンパイラによって文字列連結よりもはるかに複雑な構造に解決されることを思い出してください。

于 2011-10-07T17:10:46.037 に答える
0

遅延読み込みを使用するのに適したその他の領域は、部分的な状態で消費できるタイプです。例として、次のことを考慮してください。

public class Artist
{
     public string Name { get; set; }
     public Lazy<Manager> Manager { get; internal set; }
}

上記の例では、コンシューマーは Name プロパティを利用する必要があるだけかもしれませんが、使用されるかどうかわからないフィールドにデータを入力する必要があると、遅延読み込みが発生する可能性があります。アプリケーションが何をする必要があるかに応じて、すべてを事前にロードする方がパフォーマンスが向上する可能性がある状況が常にあるため、そうすべきでないと言います。

于 2011-10-07T17:24:48.337 に答える