更新:この質問を投稿した後、このアイデアの主な欠点は、そのような型が不適切に使用されやすいということです。つまり、利益を引き出すには、型を特定の方法で使用する必要があります。私が最初に念頭に置いていたのは、次のように使用されるものでした(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
たいとします。
- 外部の観点から不変
- 初期化が早い
- 特定のプロパティの遅延計算 (およびキャッシュ)
明らかに、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 番目の目的 (将来のアクセスのために計算結果をキャッシュする) には及ばない可能性があります。
そうです、このアイデアは、内部的に変更可能な値型に効果的につながります。しかし、外部コードからわかる限り (私が見た限りでは)、それは不変に見えますが、パフォーマンス上の利点もあります (繰り返しますが、上記の例はこのアイデアの適切な使用法ではないことに気付きました。私が話している利点は、実際にキャッシングを正当化するのに十分なコストがかかる計算に依存します)。
私は何かを見逃していますか、それともこれは実際に価値のあるアイデアですか?