views:

207

answers:

5

I have a class that defines a read-only property that effectively exposes a private field, something like this:

public class Container
{
    private List<int> _myList;

    public List<int> MyList
    {
     get { return _myList;}
    }

    public Container() : base ()
    {
     _myList = new List<int>();
    }

    // some method that need to access _myList
    public SomeMethod(int x)
    {
         _myList.Add(x);
    }
}

now it's impossible for the consumer to manage my property directly, so code like aContainer.MyList = new List(); generates a compile-time error. However, the consumer is absolutely free to call all sorts of methods on the reference he got, so this is perfectly valid code

Container c = new Container();  
Console.WriteLine(c.MyList.Count);  
c.MyList.Add(4);  
Console.WriteLine(c.MyList.Count);

which kind of defeats the whole read-only concept.

Is there any sane workaround that would enable me to have a real read-only reference propery?

P.S. I cannot just return a copy of the list because then the user will think that he made all the changes necessary, but alas... they will be gone.

+1  A: 

Have a look at the static Array.AsReadOnly() method, which you can use to return a wrapper around an array that prevents it from being modified.

Stu Mackellar
+5  A: 

Don't return a direct reference to your list. Instead return a ReadOnlyCollection wrapped around it, or if the List<> return type is set in stone, return a copy of your list. They can do whatever they want to the copy without affecting the original.

GWLlosa
+2  A: 

The reference is "readonly", the the actual object. I.e. you can't replace the reference with another object. So if you have a class that breaks it like this:

public class Container
{
    private readonly  List<int> _myList;

    public List<int> MyList
    {
        get { return _myList;}
    }

    public Container() : base ()
    {
        _myList = new List<int>();
    }

    public void BreakReadOnly()
    {
        _myList = new List<int>();
    }
}

…then it won't even compile. It's because a readonly field can't be reassigned with any other object. In this case BreakReadOnly will try to assign a new list.

If you really want a readonly collection of it then you can do it like this:

    public ReadOnlyCollection<int> MyList
    {
        get { return _myList.AsReadOnly(); }
    }

Hope this helps.

Updated: Removed use of IEnumerable.

Spoike
There is no issue with class methods accessing the private field, I just want to keep the outside world from messing with it, so using readonly on the field would only hinder me (I added SomeMethod to the question)
SWeko
The ReadOnly thing did the trick
SWeko
Updated the text. But SWeko, do you want it to be readonly as in, you can't touch the innards by anyone at all even by the owning object/class that has it? Or just by the outside?
Spoike
Ah great to hear that.
Spoike
sorry, I meant _myList.AsReadOnly(); in the real world problem I cannot change the return type (IList<>) so now I get a run-time error on Add's and Remove's but that's more than enough compared with the silent fails I was getting before
SWeko
Obviously, the Enumerable samples don't do the job properly because you can always cast them to List<int> and change the contents. Please correct this.
bruno conde
@bruno conde: Done
Spoike
A: 

You want _myList, which is a reference type to be readonly. Well, it is readonly and there is no defeat of the readonly concept.

The CLR implements a readonly property for reference types in such a way that the reference(pointer to the object if you want) is what is made readonly while the object to which the reference is pointing can be modified.

To work around this, you would need to make the member fields of the object themselves, readonly, because you cannot make the whole object readonly by attaching a readonly flag to the object's reference.

You can implement your on immutable generic collection class which would behave the same way as the List<> object but having readonly members.

Lonzo
+2  A: 

You could return a readonly Collection like this:

    public ReadOnlyCollection<int> MyList
    {
        get { return _myList.AsReadOnly(); }
    }

or return a new List so that the caller can change the list without changing the original list.

    public List<int> MyList
    {
        get { return new List<int>(_myList)}
    }
bruno conde