Являются ли типы значений неизменяемыми по определению? - PullRequest
36 голосов
/ 15 мая 2009

Я часто читаю, что struct s должны быть неизменными - не по определению?

Считаете ли вы int неизменным?

int i = 0;
i = i + 123;

Кажется, все в порядке - мы получаем новый int и присваиваем его обратно i. Как насчет этого?

i++;

Хорошо, мы можем думать об этом как о ярлыке.

i = i + 1;

А как насчет struct Point?

Point p = new Point(1, 2);
p.Offset(3, 4);

Действительно ли это изменяет точку (1, 2)? Разве мы не должны думать об этом как о сокращении для следующего с Point.Offset(), возвращающим новую точку?

p = p.Offset(3, 4);

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

Я не хочу усложнять рассуждения об этом, рассматривая параметры ref и бокс. Я также знаю, что p = p.Offset(3, 4); выражает неизменность гораздо лучше, чем p.Offset(3, 4);. Но остается вопрос - не являются ли типы значений неизменяемыми по определению?

UPDATE

Я думаю, что здесь задействованы как минимум два понятия - изменчивость переменной или поля и изменчивость значения переменной.

public class Foo
{
    private Point point;
    private readonly Point readOnlyPoint;

    public Foo()
    {
        this.point = new Point(1, 2);
        this.readOnlyPoint = new Point(1, 2);
    }

    public void Bar()
    {
        this.point = new Point(1, 2);
        this.readOnlyPoint = new Point(1, 2); // Does not compile.

        this.point.Offset(3, 4); // Is now (4, 6).
        this.readOnlyPoint.Offset(3, 4); // Is still (1, 2).
    }
}

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

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


Кажется, ответ не такой уж прямой, поэтому я перефразирую вопрос.

Учитывая следующее.

public struct Foo
{
    public void DoStuff(whatEverArgumentsYouLike)
    {
        // Do what ever you like to do.
    }

    // Put in everything you like - fields, constants, methods, properties ...
}

Можете ли вы привести готовую версию Foo и пример использования - который может включать ref параметры и упаковку - чтобы не было возможности переписать все случаи

foo.DoStuff(whatEverArgumentsYouLike);

с

foo = foo.DoStuff(whatEverArgumentsYouLike);

Ответы [ 12 ]

1 голос
/ 15 мая 2009

Нет, это не так. Пример:

Point p = new Point (3,4);
Point p2 = p;
p.moveTo (5,7);

В этом примере moveTo() - это операция на месте . Это меняет структуру, которая скрывается за ссылкой p. Вы можете увидеть это, посмотрев на p2: его позиция также изменится. При неизменных структурах moveTo() должен будет вернуть новую структуру:

p = p.moveTo (5,7);

Теперь, Point является неизменным, и когда вы создадите ссылку на него где-нибудь в вашем коде, вы не получите никаких сюрпризов. Давайте посмотрим на i:

int i = 5;
int j = i;
i = 1;

Это другое. i не является неизменным, 5 является. И второе назначение не копирует ссылку на структуру, которая содержит i, но копирует содержимое i. Так что за кулисами происходит нечто совершенно иное: вы получаете полную копию переменной, а не только копию адреса в памяти (ссылка).

Эквивалентом с объектами будет конструктор копирования:

Point p = new Point (3,4);
Point p2 = new Point (p);

Здесь внутренняя структура p копируется в новый объект / структуру и p2 будет содержать ссылку на него. Но это довольно дорогая операция (в отличие от целочисленного присваивания выше), поэтому большинство языков программирования делают это различие.

Поскольку компьютеры становятся более мощными и получают больше памяти, это различие исчезнет, ​​поскольку оно вызывает огромное количество ошибок и проблем. В следующем поколении будут только неизменные объекты, любая операция будет защищена транзакцией, и даже int будет полноценным объектом. Точно так же, как сборка мусора, это станет большим шагом вперед в стабильности программы, вызовет много горя в первые несколько лет, но позволит писать надежное программное обеспечение. Сегодня компьютеры просто недостаточно быстры для этого.

0 голосов
/ 15 мая 2009

Объекты / Структуры являются неизменными, когда они передаются в функцию таким образом, что данные не могут быть изменены, а возвращаемая структура является структурой new. Классический пример -

String s = "abc";

s.toLower();

если функция toLower написана таким образом, что возвращается новая строка, которая заменяет «s», она неизменна, но если функция идет буква за буквой, заменяя букву внутри «s» и никогда не объявляя «новую строку» , оно изменчиво.

...