Как насчет изменяемой структуры, которая неизменна с точки зрения внешнего кода? - PullRequest
0 голосов
/ 27 сентября 2010

Обновление : после публикации этого вопроса мне пришло в голову, что основным недостатком этой идеи будет просто то, что такой тип будет легко использовать неправильно. То есть тип должен использоваться очень специфическим образом для получения каких-либо преимуществ. Первоначально я имел в виду что-то, что будет использоваться следующим образом (придерживаясь примера 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. Ленивый расчет (и кеширование) определенных свойств

Очевидно, что третья характеристика подразумевает изменчивость , что, как мы все знаем, является плохим (при условии, что мантра «Не делайте изменяемые типы значений!» Была достаточно подробно просверлена в нашей голове). Но мне кажется, что это было бы приемлемо, если изменяемая часть видна только внутри самого типа, а с точки зрения внешнего кода значение всегда будет одинаковым.

Вот пример того, о чем я говорю:

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-й цели, указанной выше (кэширование вычисленного результата для будущих обращений).

Так что да, эта идея фактически приведет к типу значения, который является внутренне изменяемым. Однако, насколько мог бы сказать любой внешний код (как мне кажется), он будет казаться неизменным, в то же время принося с собой некоторые преимущества в производительности (опять же, я понимаю, что приведенный выше пример будет не будет правильным использованием этой идеи; «выигрыши в производительности», о которых я говорю, будут зависеть от того, будут ли вычисления достаточно дорогостоящими, чтобы гарантировать кэширование).

Я что-то упустил или это действительно стоящая идея?

Ответы [ 3 ]

1 голос
/ 27 сентября 2010

Нет ничего плохого в том, чтобы иметь неизменные структуры данных, которые внутренне используют мутации (при условии, что безопасность потоков хорошо рекламируется). Просто структуры копируются постоянно, поэтому с мутацией мало что можно сделать. Это не проблема в C / C ++, потому что структуры обычно передаются по ссылке, тогда как в C # редко передаются структуры по ссылке. Поскольку сложно рассуждать о структурах, которые передаются по значению, изменяемые структуры не приветствуются в C #.

1 голос
/ 27 сентября 2010

То, что вы описываете, может, вероятно, работать несколько, но с важным предостережением: результат медленного вычисления может сохраняться или не сохраняться при его вычислении. Например, если вычисление выполняется для структур, возвращаемых перечислением, результаты будут храниться во «временных» структурах и будут отбрасываться, а не распространяться обратно на структуры, хранящиеся в структуре данных.

0 голосов
/ 27 сентября 2010

Это похоже на Future . Было упомянуто о лучшей поддержке Futures в параллельных расширениях C # 4.0. (Как вычисление их на другом ядре / потоке параллельно с вашей обычной работой).

...