views:

1257

answers:

8

Is it possible to somehow mark a System.Array as immutable. When put behind a public-get/private-set they can't be added to, since it requires re-allocation and re-assignment, but a consumer can still set any subscript they wish:

public class Immy
{
    public string[] { get; private set; }
}

I thought the readonly keyword might do the trick, but no such luck.

+3  A: 

I believe best practice is to use IList<T> rather than arrays in public APIs for this exact reason. readonly will prevent a member variable from being set outside of the constructor, but as you discovered, won't prevent people from assigning elements in the array.

See Arrays Considered Somewhat Harmful for more information.

Edit: Arrays can't be read only, but they can be converted to read-only IList implementations via Array.AsReadOnly() as @shahkalpesh points out.

Matt
That's an excellent article.
Bob King
+12  A: 

ReadOnlyCollection is probably what you are looking for. It doesnt have an Add() method.

http://msdn.microsoft.com/en-us/library/ms132474.aspx

Ray Jezek
More accurately, it doesn't have a publicly exposed Add() method. It does have one, because the interfaces require it. But good interface code will check .ReadOnly first before calling it, because doing so would cause an exception.
Matthew Scharley
+4  A: 

You could use Array.AsReadOnly method to return.

shahkalpesh
+12  A: 

No, that's not possible in .NET.

The Framework Design Guidelines suggest returning a copy of the Array. That way, consumers can't change items from the array.

// bad code
// could still do Path.InvalidPathChars[0] = 'A';
public sealead class Path {
   public static readonly char[] InvalidPathChars = 
      { '\"', '<', '>', '|' };
}

these are better:

public static ReadOnlyCollection<char> GetInvalidPathChars(){
   return Array.AsReadOnly(badChars);
}

public static char[] GetInvalidPathChars(){
   return (char[])badChars.Clone();
}

The examples are straight from the book.

Quantenmechaniker
A: 

The only thing to add is that Arrays imply mutability. When you return an Array from a function, you are suggesting to the client programmer that they can/should change things.

Travis
Is this a convention or is there a more specific reason for clients expecting this behaviour?
biozinc
A: 

Further to Matt's answer, IList is a complete abstract interface to an array, so it allows add, remove, etc. I'm not sure why Lippert appears to suggest it as an alternative to IEnumerable where immutability is needed. (Edit: because the IList implementation can throw exceptions for those mutating methods, if you like that kind of thing).

Maybe another thing to bear in mind that the items on the list may also have mutable state. If you really don't want the caller to modify such state, you have some options:

Make sure the items on the list are immutable (as in your example: string is immutable).

Return a deep clone of everything, so in that case you could use an array anyway.

Return an interface that gives readonly access to an item:

interface IImmutable
{
    public string ValuableCustomerData { get; }
}

class Mutable, IImmutable
{
    public string ValuableCustomerData { get; set; }
}

public class Immy
{
    private List<Mutable> _mutableList = new List<Mutable>();

    public IEnumerable<IImmutable> ImmutableItems
    {
        get { return _mutableList.Cast<IMutable>(); }
    }
}

Note that every value accessible from the IImmutable interface must itself be immutable (e.g. string), or else be a copy that you make on-the-fly.

Daniel Earwicker
A: 

The best you can hope to do is extend an existing collection to build your own. The big issue is that it would have to work differently than every existing collection type because every call would have to return a new collection.

Rick Minerich
A: 

You might want to check out my answer to a similar question for more ideas on exposing collections on an object.

Emperor XLII