Обновление : после публикации этого вопроса мне пришло в голову, что основным недостатком этой идеи будет просто то, что такой тип будет легко использовать неправильно. То есть тип должен использоваться очень специфическим образом для получения каких-либо преимуществ. Первоначально я имел в виду что-то, что будет использоваться следующим образом (придерживаясь примера 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
, который я хочу иметь следующие характеристики:
- Неизменный от внешней точки обзора
- Быстрая инициализация
- Ленивый расчет (и кеширование) определенных свойств
Очевидно, что третья характеристика подразумевает изменчивость , что, как мы все знаем, является плохим (при условии, что мантра «Не делайте изменяемые типы значений!» Была достаточно подробно просверлена в нашей голове). Но мне кажется, что это было бы приемлемо, если изменяемая часть видна только внутри самого типа, а с точки зрения внешнего кода значение всегда будет одинаковым.
Вот пример того, о чем я говорю:
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
почти наверняка не является достаточно дорогостоящим, чтобы считать этот подход целесообразным в данном случае. Это только пример для иллюстрации.
Но я думаю, что это решает три мои задачи, которых не было бы наиболее очевидным альтернативным подходам. В частности:
- I может выполнить вычисление в конструкторе типа; но это потенциально может не соответствовать 2-й цели, указанной выше (быстрая инициализация).
- I может выполнять вычисления при каждом доступе к недвижимости; но это потенциально может не соответствовать 3-й цели, указанной выше (кэширование вычисленного результата для будущих обращений).
Так что да, эта идея фактически приведет к типу значения, который является внутренне изменяемым. Однако, насколько мог бы сказать любой внешний код (как мне кажется), он будет казаться неизменным, в то же время принося с собой некоторые преимущества в производительности (опять же, я понимаю, что приведенный выше пример будет не будет правильным использованием этой идеи; «выигрыши в производительности», о которых я говорю, будут зависеть от того, будут ли вычисления достаточно дорогостоящими, чтобы гарантировать кэширование).
Я что-то упустил или это действительно стоящая идея?