типы неизменных значений - PullRequest
7 голосов
/ 04 июля 2011

Я читаю блог Эрика Липертса о Mutating Readonly Structs , и я вижу много ссылок здесь в SO на этот блог в качестве аргумента, почему типы значений должны быть неизменными. Но все же одно неясно, говорит, что при доступе к типу значения вы всегда получаете его копию, и вот пример:

struct Mutable
{
    private int x;
    public int Mutate()
    {
        this.x = this.x + 1;
        return this.x;
    }
}

class Test
{
    public readonly Mutable m = new Mutable();
    static void Main(string[] args)
    {
        Test t = new Test();
        System.Console.WriteLine(t.m.Mutate());
        System.Console.WriteLine(t.m.Mutate());
        System.Console.WriteLine(t.m.Mutate());
    }
}

И вопрос в том, почему, когда я меняю

public readonly Mutable m = new Mutable();

до

public Mutable m = new Mutable();

все начинает работать, если ожидается .

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

Ответы [ 4 ]

4 голосов
/ 04 июля 2011

Структуры с методами мутации ведут себя странно в нескольких ситуациях.

Пример, который вы уже обнаружили, - это поле только для чтения. Защитная копия необходима, потому что вы не хотите изменять поле только для чтения.

Но также при использовании в качестве свойств. Еще раз неявная копия происходит, и только копия видоизменяется. Даже если у свойства есть сеттер.

struct Mutable
{
    private int x;
    public int Mutate()
    {
        this.x = this.x + 1;
        return this.x;
    }
}

Mutable property{get;set;}

void Main()
{
    property=new Mutable();
    property.Mutate().Dump();//returns 1
    property.Mutate().Dump();//returns 1 :(
}

Это показывает, что методы мутации проблематичны для структур. Но это не показывает, что изменяемая структура с открытыми полями или свойствами, имеющими установщик, является проблематичной.

2 голосов
/ 04 июля 2011

Я не знаю C #, поэтому я постараюсь ответить на 2 часть вашего вопроса.

Почему типы значений должны быть неизменяемыми?

Существует два типа объектов с точки зрения доменного дизайна:

  • значения объектов / типов - их идентичность определяется их значением (например, числа: 2 всегда 2 - идентичность числа два всегда одинакова, поэтому 2 == 2 всегда верно)
  • сущности (ссылочные типы) - они могут состоять из других типов значений, и их идентичность определяется самой их идентичностью (например, людьми: даже если бы был человек, похожий на вас, это не было бы вы)

Если бы типы значений были изменяемыми, то представьте, что могло бы произойти, если бы можно было изменить значение числа два: 2 == 1 + 1 не гарантировалось бы как истинное.

См. Эти ссылки для получения дополнительной информации:

2 голосов
/ 04 июля 2011

Резьба безопасности является очевидной технической причиной.Это относится как к типам значений, так и к ссылочным типам (см. System.String).

Более общее руководство "типы значений должны быть неизменяемыми" отличается.Речь идет о читабельности кода, и в основном из-за путаницы, которую могут вызывать изменяемые значенияЭтот фрагмент кода является лишь одним примером.Большинство людей не ожидали бы результата 1,1,1.

0 голосов
/ 04 июля 2011

Я думаю, что хитрый момент в этом примере заключается в том, что можно утверждать, что это невозможно.Вы сделали экземпляр Mutable доступным только для чтения, и все же вы можете изменить его значение с помощью функции Mutate (), поэтому в некотором смысле нарушаете концепцию неизменности.Строго говоря, однако, это работает, потому что закрытое поле x не только для чтения.Если вы сделаете одно простое изменение в изменчивом классе, тогда фактически будет применяться неизменность:

private readonly int x;

Тогда функция Mutate () выдаст ошибку компилятора.

Этот пример ясно показывает, как копирование по значению работает в контексте переменных только для чтения.Всякий раз, когда вы вызываете m, вы создаете копию экземпляра, в отличие от копии ссылки на экземпляр - последнее произойдет, если Mutable был классом, а не структурой.

Поскольку каждый раз, когда вы вызываете m, вы вызываете 1) копию экземпляра и 2) копию экземпляра, доступного только для чтения, значение x равно , всегда равным 0 привремя копирования .Когда вы вызываете Mutate () для копии, он увеличивает x на 1, что работает, потому что сам x НЕ доступен только для чтения.Но в следующий раз, когда вы вызываете Mutate (), вы все равно вызываете его с исходным значением по умолчанию, равным 0. Как он говорит в статье, «m является неизменным, а копия - нет».Каждая копия исходного экземпляра будет иметь x как 0, потому что копируемый объект никогда не изменяется, тогда как его копии могут быть изменены.

Может быть, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...