6

I'm trying using ReadOnlyCollection to make object immutable, I want the property of object are immutable.

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

But I little confused.

I tried to change the property of the object in to MyReadOnlyList using a foreach and ... I can change value property, is it correct? I understood ReadOnlyCollection set an add level to make the object immutable.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
lunatic84
  • 300
  • 4
  • 12

3 Answers3

13

The fact that ReadOnlyCollection is immutable means that the collection cannot be modified, i.e. no objects can be added or removed from the collection. This does not mean that the objects it contains immutable.

This article by Eric Lippert, explains how different kinds of immutability work. Basically, a ReadOnlyCollection is an immutable facade which can read the underlying collection (_myDataList), but cannot modify it. However, you can still change the underlying collection since you have a reference to _myDataList by doing something like _myDataList[0] = null.

Furthermore, the objects returned by ReadOnlyCollection are the same ones returned by _myDataList, i.e. this._myDataList.First() == this.MyReadOnlyList.First() (with LINQ). This means that if an object in _myDataList is mutable, then so is the object in MyReadOnlyList.

If you want the objects to be immutable, you should design them accordingly. For instance, you might use:

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; }
}

instead of:

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

Edit: in this case, as noted by Ian Goldby, neither struct allows you to modify properties of the elements in the collection. This happens because structs are value types and when you access an element the collection returns a copy of the value. You can only modify the properties of a Point type if it is a class, which would mean that references to the actual objects are returned, instead of copies of their values.

Todd
  • 12,995
  • 3
  • 30
  • 25
Fernando Matsumoto
  • 2,697
  • 1
  • 18
  • 24
  • Actually, if the objects in the ReadOnlyCollection are structs rather than classes then they are effectively read-only in the collection because accessing an element gives you a *boxed* struct. You can't modify this directly ("Cannot modify the result of an unboxing conversion") and if you assign it to a local struct variable you get a copy. (You can still modify a ReadOnlyCollection via the Items property though.) – Ian Goldby Jan 07 '16 at 09:16
  • Why would the values returned by the collection be boxed, if the collection is generic and returns elements of type `T`? Apart from that, I've updated my answer to say that structs in the collection are effectively immutable, since you can only get copies of their values. – Fernando Matsumoto Jan 07 '16 at 13:10
  • I suspect it is an optimisation by the framework to prevent a copy of the struct being made when you are just reading from it. Certainly, if you write `roList[i].X = 5` the compiler complains "Cannot modify the result of an unboxing conversion". If the compiler didn't do this, then `x = roList[i].X` would always entail making a complete copy of the struct just to read a single property. – Ian Goldby Jan 07 '16 at 13:34
  • @IanGoldby "I suspect it is an optimisation by the framework to prevent a copy of the struct being made when you are just reading from it." - Why do you think the framework does that? I don't think it does. A ReadOnlyCollection uses indexers to return values which would return a copy of the struct. – David Klempfner May 07 '21 at 03:35
2

I tried to change the property of the object in to MyReadOnlyList using a foreach and ... I can change value property, is it correct? I understood ReadOnlyCollection set an add level to make the object immutable.

Using a ReadOnlyCollection does not make any guarantees as for the object that is stored in the collection. All it guarantees is that the collection cannot be modified once it has been created. If an element is retrieved from it, and it has mutable properties, it can very well be modified.

If you want to make your FooObject an immutable one, then simply do so:

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

    public string SomeString { get; };
    public int SomeInt { get; };
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
1

What is immutable is the collection itself, not the objects. For now, C# doesn't support immutable objects without wrapping them as ReadOnlyCollection<T> does in your case.

Well, you can still create immutable objects if their properties have no accessible setter. BTW, they're not immutable at all because they can mutate from a class member that may have equal or more accessibility than the setter.

// 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";
}

As I said before, reference types are always mutable by definition and they can behave as immutable under certain conditions playing with member accessibility.

Finally, structs are immutable but they're value types and they shouldn't be used just because they can represent immutable data. See this Q&A to learn more about why structs are immutable: Why are C# structs immutable?

Community
  • 1
  • 1
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • I red this in a book but I think it is wrong >The System.Collections.ObjectModel.ReadOnlyCollection type is the standard way to wrap a collection and export a read-only version of that data – lunatic84 Sep 07 '15 at 12:48
  • @lunatic84 That definition says what my answer is trying to address for you. The collection is immutable but the data is still mutable. – Matías Fidemraizer Sep 07 '15 at 12:50