0

更新:この質問を投稿した後、このアイデアの主な欠点は、そのような型が不適切に使用されやすいということです。つまり、利益を引き出すには、型を特定の方法で使用する必要があります。私が最初に念頭に置いていたのは、次のように使用されるものでした(SquareRootStruct一貫性のために元の質問の例に固執します):

class SomeClass
{
    SquareRootStruct _key;

    public SomeClass(int value)
    {
        _key = new SquareRootStruct(value);
    }

    public double SquareRoot
    {
        // External code would have to access THIS property for caching
        // to provide any benefit.
        get { return _key.SquareRoot; }
    }

    public SquareRootStruct GetCopyOfKey()
    {
        // If _key has cached its calculation, then its copy will carry
        // the cached results with it.
        return _key;
    }
}

// elsewhere in the code...
var myObject = new SomeClass();

// If I do THIS, only a COPY of myObject's struct is caching a calculation,
// which buys me nothing.
double x = myObject.GetCopyOfKey().SquareRoot;

// So I would need to do this in order to get the benefit (which is,
// admittedly, confusing)...
double y = myObject.SquareRoot;

したがって、これがどれほど簡単に間違いを犯すかを考えると、これはクラスとしてより理にかなっているというリードの (彼のコメントでの) 正しい可能性があると考える傾向があります。


次の特性を持ちstructたいとします。

  1. 外部の観点から不変
  2. 初期化が早い
  3. 特定のプロパティの遅延計算 (およびキャッシュ)

明らかに、3 番目の特性はmutabilityを意味しますが、これが悪いことであることは誰もが知っています (「変更可能な値の型を作成しないでください!」というマントラが十分に頭にドリルされていると仮定します)。しかし、変更可能な部分が型自体の内部でのみ可視であり、外部コードの観点からは値が常に同じである限り、これは受け入れられるように思えます。

これが私が話していることの例です:

struct SquareRootStruct : IEquatable<SquareRootStruct>
{
    readonly int m_value;  // This will never change.

    double m_sqrt;         // This will be calculated on the first property
                           // access, and thereafter never change (so it will
                           // appear immutable to external code).

    bool m_sqrtCalculated; // This flag will never be visible
                           // to external code.

    public SquareRootStruct(int value) : this()
    {
        m_value = value;
    }

    public int Value
    {
        get { return m_value; }
    }

    public double SquareRoot
    {
        if (!m_sqrtCalculated)
        {
            m_sqrt = Math.Sqrt((double)m_value);
            m_sqrtCalculated = true;
        }

        return m_sqrt;
    }

    public bool Equals(SquareRootStruct other)
    {
        return m_value == other.m_value;
    }

    public override bool Equals(object obj)
    {
        return obj is SquareRootStruct && Equals((SquareRootStruct)obj);
    }

    public override int GetHashCode()
    {
        return m_value;
    }
}

さて、明らかにこれは些細な例Math.Sqrtあり、この場合、このアプローチが価値があると考えるほどコストがかからないことはほぼ確実です。これは説明のための例にすぎません。

しかし、私の考えでは、これにより、最も明白な代替アプローチでは実現できない私の 3 つの目的が達成されると思います。具体的には:

  • 型のコンストラクターで計算を実行できます。しかし、これは上記の 2 番目の目的 (初期化が速い) には及ばない可能性があります。
  • すべてのプロパティ アクセスに対して計算を実行できました。しかし、これは上記の 3 番目の目的 (将来のアクセスのために計算結果をキャッシュする) には及ばない可能性があります。

そうです、このアイデアは、内部的に変更可能な値型に効果的につながります。しかし、外部コードからわかる限り (私が見た限りでは)、それは不変に見えますが、パフォーマンス上の利点もあります (繰り返しますが、上記の例はこのアイデアの適切な使用法ではないことに気付きました。私が話している利点は、実際にキャッシングを正当化するのに十分なコストがかかる計算に依存します)。

私は何かを見逃していますか、それともこれは実際に価値のあるアイデアですか?

4

3 に答える 3

1

内部でミューテーションを使用する不変のデータ構造を持つことには何の問題もありません (スレッドの安全性が十分に宣伝されている限り)。構造体が常にコピーされるだけなので、ミューテーションではあまりできません。C/C++ では通常、構造体は参照渡しされるため、これは問題になりませんが、C# では構造体を参照渡しすることはめったにありません。値渡しされる構造体について推論するのは難しいため、可変構造体は C# では推奨されません。

于 2010-09-27T19:51:50.080 に答える
1

あなたが説明したことはおそらくいくらか機能する可能性がありますが、重要な注意点があります。遅い計算の結果は、計算時に保存される場合と保存されない場合があります。たとえば、列挙によって返された構造体に対して計算が実行される場合、結果は「一時的な」構造体に格納され、データ構造に格納されている構造体に反映されるのではなく破棄されます。

于 2010-09-27T19:55:00.187 に答える
0

これはFutureに似ています。C#4.0並列拡張機能でのFuturesのサポートの改善についての言及がいくつかありました。(通常の作業と並行して別のコア/スレッドでそれらを計算するように)。

于 2010-09-27T19:31:09.630 に答える