ReadonlyCollection, неизменяемы ли объекты?

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

public ReadOnlyCollection<FooObject> MyReadOnlyList
{
    get
    {
        return new ReadOnlyCollection<FooObject>(_myDataList);
    }
}

Но я немного запутался.

Я попытался изменить свойство объекта на MyReadOnlyList с помощью foreach и ... Я могу изменить свойство value, это правильно? Я понял, что ReadOnlyCollection устанавливает уровень добавления, чтобы сделать объект неизменным.


person lunatic84    schedule 07.09.2015    source источник


Ответы (3)


Тот факт, что ReadOnlyCollection неизменяем, означает, что коллекцию нельзя изменить, т.е. нельзя добавлять или удалять объекты из коллекции. Это не означает, что содержащиеся в нем объекты неизменяемы.

Эта статья Эрика Липперта, объясняет, как работают разные виды неизменяемости. По сути, ReadOnlyCollection - это неизменный фасад, который может читать базовую коллекцию (_myDataList), но не может ее изменять. Однако вы все равно можете изменить базовую коллекцию, поскольку у вас есть ссылка на _myDataList, выполнив что-то вроде _myDataList[0] = null.

Кроме того, объекты, возвращаемые ReadOnlyCollection, совпадают с объектами, возвращаемыми _myDataList, то есть this._myDataList.First() == this.MyReadOnlyList.First()LINQ). Это означает, что если объект в _myDataList является изменяемым, то и объект в MyReadOnlyList тоже.

Если вы хотите, чтобы объекты были неизменными, вы должны соответствующим образом их спроектировать. Например, вы можете использовать:

public struct Point
{
    public Point(int x, int y)
    {
        this.X = x;
        this.Y = y;
    }

    // In C#6, the "private set;" can be removed
    public int X { get; private set; }
    public int Y { get; private set; }
}

вместо:

public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

Изменить: в этом случае, как отметил Ян Голдби, ни одна из структур не позволяет вам изменять свойства элементов в коллекция. Это происходит потому, что структуры являются типами значений, и когда вы обращаетесь к элементу, коллекция возвращает копию значения. Вы можете изменять свойства типа Point, только если это класс, что будет означать, что будут возвращены ссылки на фактические объекты, а не на копии их значений.

person Fernando Matsumoto    schedule 07.09.2015
comment
Фактически, если объекты в ReadOnlyCollection являются структурами, а не классами, тогда они фактически доступны только для чтения в коллекции, потому что доступ к элементу дает вам структуру в штучной упаковке. Вы не можете изменить это напрямую (не можете изменить результат преобразования распаковки), и если вы назначите его локальной переменной структуры, вы получите копию. (Однако вы все равно можете изменить ReadOnlyCollection через свойство Items.) - person Ian Goldby; 07.01.2016
comment
Почему значения, возвращаемые коллекцией, помещаются в коробку, если коллекция является универсальной и возвращает элементы типа T? Кроме того, я обновил свой ответ, чтобы сказать, что структуры в коллекции фактически неизменяемы, поскольку вы можете получать только копии их значений. - person Fernando Matsumoto; 07.01.2016
comment
Я подозреваю, что это оптимизация фреймворка, чтобы предотвратить создание копии структуры, когда вы просто читаете ее. Конечно, если вы напишете roList[i].X = 5, компилятор пожалуется, что не может изменить результат преобразования распаковки. Если компилятор этого не делает, x = roList[i].X всегда влечет за собой создание полной копии структуры только для чтения одного свойства. - person Ian Goldby; 07.01.2016
comment
@IanGoldby Я подозреваю, что это оптимизация фреймворка, чтобы предотвратить создание копии структуры, когда вы просто читаете ее. - Как вы думаете, почему фреймворк это делает? Я не думаю, что это так. ReadOnlyCollection использует индексаторы для возврата значений, которые вернут копию структуры. - person David Klempfner; 07.05.2021

Я попытался изменить свойство объекта на MyReadOnlyList с помощью foreach и ... Я могу изменить свойство value, это правильно? Я понял, что ReadOnlyCollection устанавливает уровень добавления, чтобы сделать объект неизменным.

Использование ReadOnlyCollection не дает никаких гарантий относительно объекта, хранящегося в коллекции. Все, что это гарантирует, - это то, что коллекцию нельзя изменить после того, как она была создана. Если из него извлекается элемент и у него изменяемые свойства, его можно очень хорошо изменить.

Если вы хотите сделать свой FooObject неизменяемым, просто сделайте это:

public class FooObject
{
    public FooObject(string someString, int someInt)
    {
        SomeString = someString;
        SomeInt = someInt;
    }

    public string SomeString { get; };
    public int SomeInt { get; };
}
person Yuval Itzchakov    schedule 07.09.2015

Неизменяемым является сама коллекция, а не объекты. На данный момент C # не поддерживает неизменяемые объекты без их обертывания, как это делает ReadOnlyCollection<T> в вашем случае.

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

// Case 1
public class A
{
    public string Name { get; private set; }

    public void DoStuff() 
    {
        Name = "Whatever";
    }
}

// Case 2
public class A
{
    // This property will be settable unless the code accessing it
    // lives outside the assembly where A is contained...
    public string Name { get; internal set; }
}

// Case 3
public class A
{
    // This property will be settable in derived classes...
    public string Name { get; protected set; }
}

// Case 4: readonly fields is the nearest way to design an immutable object
public class A
{
     public readonly string Text = "Hello world";
}

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

Наконец, структуры неизменяемы, но являются типами значений, и их не следует использовать только потому, что они могут представлять неизменяемые данные. См. Эти вопросы и ответы, чтобы узнать больше о том, почему структуры неизменяемы: Почему структуры C # неизменяемы?

person Matías Fidemraizer    schedule 07.09.2015
comment
Я написал об этом в книге, но считаю, что это неправильно. ›Тип System.Collections.ObjectModel.ReadOnlyCollection ‹T› - это стандартный способ обернуть коллекцию и экспортировать версию этих данных только для чтения - person lunatic84; 07.09.2015
comment
@ lunatic84 Это определение говорит о том, что мой ответ пытается адресовать вам. Коллекция неизменна, но данные по-прежнему изменчивы. - person Matías Fidemraizer; 07.09.2015