Dathan
Dathan

Reputation: 7446

Does C#/CLR contain a mechanism for marking the return values of properties as read-only / immutable?

I've been looking around, and so far haven't managed to find a good way to do this. It's a common problem, I'm sure.

Suppose I have the following:

class SomeClass : IComparable
{ 
    private int myVal; 
    public int MyVal
    { 
        get { return myVal; } 
        set { myVal = value; }
    }

    public int CompareTo(object other) { /* implementation here */ }
}

class SortedCollection<T>
{
    private T[] data;
    public T Top { get { return data[0]; } }

    /* rest of implementation here */
}

The idea being, I'm going to implement a binary heap, and rather than only support Insert() and DeleteMin() operations, I want to support "peeking" at the highest (or lowest, as the case may be) priority value on the stack. Never did like Heisenberg, and that whole "you can't look at things without changing them" Uncertainty Principle. Rubbish!

The problem, clearly, is that the above provides no means to prevent calling code from modifying MyVal (assuming SortedCollection) via the Top property, which operation has the distinct possibility of putting my heap in the wrong order. Is there any way to prevent modifications from being applied to the internal elements of the heap via the Top property? Or do I just use the code with a warning: "Only stable if you don't modify any instances between the time they're inserted and dequeue'd. YMMV."

Upvotes: 1

Views: 221

Answers (5)

Tam&#225;s Szelei
Tam&#225;s Szelei

Reputation: 23941

I understand your problem now. I think this should work:

class SortedCollection<T> where T: ICloneable
{
    private T[] data;
    public T Top 
    { 
         get 
         { 
             T ret = (T)data[0].Clone();
             return ret; 
         }
    }

    /* rest of implementation here */
}

The ICloneable constraint ensures that the type parameter implements the ICloneable interface. (if this is acceptable)

Upvotes: 0

Mike Dinescu
Mike Dinescu

Reputation: 55730

To answer your question: No, there's no way to implement the kind of behavior you want - as long as T is of reference type (and possibly even with some value-types)

You can't really do much about it. As long as you provide a getter, calling code can modify the internal contents of your data depending on the accessibility of said data (i.e. on properties, fields, and methods).

class SomeClass : IComparable
{ 
    private int myVal; 
    public int MyVal
    { 
        get { return myVal; } 
        set { myVal = value; }
    }

    public int CompareTo(object other) { /* implementation here */ }
}


class SortedCollection<T>
{
    private T[] data;
    public T Top { get { return data[0]; } }

    /* rest of implementation here */
}

//..
// calling code
SortedCollection<SomeClass> col;
col.Top.MyVal = 500;  // you can't really prevent this

NOTE What I mean is you can't really prevent it in the case of classes that you don't control. In the example, like others have stated you can make MyVal's set private or omit it; but since SortedColleciton is a generic class, you can't do anything about other people's structures..

Upvotes: 1

Sam Harwell
Sam Harwell

Reputation: 99889

Your properties don't have to have the same accessibility for get/set. This covers you for anything that returns a value type (typically structs that only contain value types) or immutable reference types.

public int MyVal
{ 
    get { return myVal; } 
    private set { myVal = value; }
}

For mutable reference types, you have other options, such as returning Clone()s or using ReadOnlyCollection<T> to keep the caller from changing them:

private List<int> data;

public IList<int> Data
{
    get { return new ReadOnlyCollection<int>(this.data); }
}

Upvotes: 1

Mircea Grelus
Mircea Grelus

Reputation: 2915

Only implement getters for your properties and modify the collection by having add/remove methods

Upvotes: 0

Joel Coehoorn
Joel Coehoorn

Reputation: 415901

You can have a readonly property (that is, a property with only a getter):

private int myVal;
public int MyVal { get { return myVal; } }

But be careful: this may not always work how you expect. Consider:

private List<int> myVals;
public List<int> MyVals { get { return myVals; } }

In this case, you can't change which List the class uses, but you can still call that List's .Add(), .Remove(), etc methods.

Upvotes: 1

Related Questions