Подтвердить что ты не робот

Используется ли использование общедоступных полей readonly для неизменяемых структур?

Является ли это правильным способом объявления неизменяемых структур?

public struct Pair
{
    public readonly int x;
    public readonly int y;

    // Constructor and stuff
}

Я не могу придумать, почему это столкнулось с проблемами, но я просто хотел попросить вас убедиться.

В этом примере я использовал ints. Что делать, если я использовал класс вместо этого, но этот класс также неизменен, например? Это тоже должно работать нормально?

public struct Pair
{
    public readonly (immutableClass) x;
    public readonly (immutableClass) y;

    // Constructor and stuff
}

(Кроме того: я понимаю, что использование свойств более обобщаемо и позволяет изменять, но эта структура предназначена буквально для того, чтобы просто хранить два значения. Меня просто интересует вопрос непреложности здесь.)

4b9b3361

Ответ 1

Если вы собираетесь использовать структуры, лучше всего сделать их неизменными.

Создание всех полей readonly - отличный способ помочь (1) документу, что структура является неизменной и (2) предотвращает случайные мутации.

Однако есть одна морщина, которая на самом деле в странном совпадении я планировал вести блог о следующей неделе. То есть: readonly в поле struct является ложью. Можно ожидать, что поле readonly не может измениться, но, конечно, это возможно. "readonly" в поле struct - это декларация, которая записывает чеки без денег в своем аккаунте. Структура не имеет своего хранилища, и это хранилище, которое может мутировать.

Например, возьмите вашу структуру:

public struct Pair
{
    public readonly int x;
    public readonly int y;
    public Pair(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    public void M(ref Pair p)
    {
        int oldX = x;
        int oldY = y;
        // Something happens here
        Debug.Assert(x == oldX);
        Debug.Assert(y == oldY);
    }
}

Есть ли что-нибудь, что может произойти при "что-то происходит здесь", что приводит к нарушению утверждений отладки? Конечно.

    public void M(ref Pair p)
    {
        int oldX = this.x;
        int oldY = this.y;
        p = new Pair(0, 0);
        Debug.Assert(this.x == oldX);
        Debug.Assert(this.y == oldY);
    }
...
    Pair myPair = new Pair(10, 20);
    myPair.M(ref myPair);

А теперь что происходит? Утверждение нарушено! "this" и "p" относятся к тому же месту хранения. Место хранения мутировано, поэтому содержимое "this" мутировано, потому что это одно и то же. Структура не может обеспечить доступность только для чтения x и y, поскольку структура не владеет хранилищем; хранилище - это локальная переменная, которая может мутировать столько, сколько захочет.

Вы не можете полагаться на инвариант, который никогда не наблюдается для поля readonly в структуре; единственное, на что вы можете положиться, это то, что вы не можете писать код, который его напрямую меняет. Но, немного подкрадывая эту работу, вы можете косвенно изменить все, что захотите.

См. также Джо Доффи отличную статью в блоге по этой проблеме:

http://joeduffyblog.com/2010/07/01/when-is-a-readonly-field-not-readonly/

Ответ 2

Это сделало бы его неизменным. Полагаю, вам лучше добавить конструктор. Если все его члены также неизменны, это сделает его полностью неизменным. Это могут быть классы или простые значения.

Ответ 3

Компилятор запретит присвоение полям readonly, а также свойствам только для чтения.

Я рекомендую использовать свойства только для чтения, в основном, для общедоступных интерфейсов и привязки данных (которые не будут работать с полями). Если бы это был мой проект, я бы потребовал, чтобы структура/класс была общедоступной. Если он будет внутренним для сборки или закрыт для класса, я мог бы сначала его игнорировать и реорганизовать их свойства только для чтения.