466790 (5) [Avatar] Offline
#1
After reading the author's comments on how well we can achieve an immutable class with C# 6. I tried the following code

class ImmutableTest
    {
        
        public List<int> immutableList => new List<int>();

        public List<int> readonlyList { get; private set; }
    }


And the caller writes

ImmutableTest o = new ImmutableTest();
            //1: gives no error but does not add the element in the list
            o.immutableList.Add(1);

            //2: gives runtime error
            o.readonlyList.Add(1);


I feel that "1" is more dangerous than "2" because "1" neither does what is expected nor does it throw an exception. I would request the author to please explain how to write an immutable class
ImmutableTest
so that
o.immutableList.Add(1);
does throw an exception.
jon.skeet (446) [Avatar] Offline
#2
This doesn't feel like it's particularly related to expression-bodied members - you have the same problem before C# 6.

*Both* of your options are broken if you're looking for immutability: your second option doesn't achieve immutability either, as you're allowing the caller to mutate the list.

It's not clear what you want your class to achieve, but if you want a property that returns a read-only list, you shouldn't be declaring that it returns `List<T>` - declare that you return something like `IList<T>`, `IReadOnlyList<T>`, `ReadOnlyCollection<T>` or `IImmutableList<T>`.

466790 (5) [Avatar] Offline
#3
I understand that I could not write

public List<int> immutableList => new List<int>();


before C# 6.

I agree that I should declare ReadonlyCollection<T> et al. for achieving readonly behaviour.

The question I am trying to ask is isn't the list that I declare "readonly" and "immutable", so why does it not throw an exception when I try to add a new element in the list.
jon.skeet (446) [Avatar] Offline
#4
You couldn't write that code, but you could write the equivalent:

public List<int> immutableList  { get { return new List<int>(); } }


... which would have the exact same effect. So basically, you shouldn't do that. In both cases, it makes perfect sense to be able to call the property and then call Add on the returned reference. I don't know which part of the language you'd expect to prevent at compile-time, or why you'd expect an exception to be thrown at execution time.

If you want a read-only property, use

public List<int> ReadOnlyProperty { get; } = new List<int>();


instead. That's an automatically-implemented read-only property, and it's very different in effect from the property you showed before - but from a *caller's* perspective, it's the same, in terms of just being "a read-only property of type List<int>.