Immutability is not an end goal, in and of itself. Immutability is a tool. Like all tools, it has its appropriate uses, its advantages and disadvantages. Achieving it with existing mutable objects is harder than it looks.
Consider an ordinary array:
var pokerHand = new Cards[5];
We can make a data structure using an array as a backing field, and writing an indexer for it:
public class ImmutableArray<T>
{
T[] array;
public this T(int index)
{
get { return array[index]; }
}
}
By eliminating the setter from the indexer, you've effectively made member access immutable, right? Well, not exactly. Because the item returned by the indexer is a reference type, you can still replace the object held by that reference type, and now you have a new object in that position in the array.
var item = myPokerHand[5];
item = new Card("KS"); // myPokerHand[5] now contains a new Card
To protect the contents of the original array, you have to make a copy of the object, and return that instead.
public this T(int index)
{
get { return array[index].MemberwiseClone(); } // if you can.
}
Note that MemberwiseClone is a shallow copy, not a deep copy. Doing this correctly could mean writing a Clone method that faithfully copies all of your object's members, some of which may also be mutable references.
For all of these reasons, it is better to use the immutable collections already provided by the .NET framework, such as ReadOnlyCollection, collections that are designed from the ground up to be immutable.
For your own collections and objects, routinely restricting access to private members is generally a good thing, for reasons that I hope are obvious (cf. encapsulation).
Further Reading
Immutability in C# Part One: Kinds of Immutability