If you want to ban the modifying operations at compile time, you need a type-safe solution.
Declare an interface for the publicly allowed operations. Use that interface as the property type.
public interface IReadOnlyList<T>
{
T this[int index] { get; }
int Count { get; }
}
Then declare a class that implements that interface and inherits from the standard collection class.
public class SafeList<T> : List<T>, IReadOnlyList<T> { }
Assuming you get the interface definition right, you won't need to implement anything by hand, as the base class already provides the implementations.
Use that derived class as the type of the field that stores the property value.
public class A
{
private SafeList<string> _list = new SafeList<string>();
public IReadOnlyList<string>
{
get { return _list; }
}
}
Within class A, you can use _list
directly, and so modify the contents. Clients of class A will only be able to use the subset of operations available via IReadOnlyList<T>
.
For your example, you're using SortedList instead of List, so the interface probably needs to be
public interface IReadOnlyDictionary<K, V> : IEnumerable<KeyValuePair<K, V>>
{
V this[K index] { get; }
}
I've made it inherit IEnumerable as well, which is readonly anyway, so is perfectly safe. The safe class would then be:
public class SafeSortedList<K, V> : SortedList<K, V>, IReadOnlyDictionary<K, V> { }
But otherwise it's the same idea.
Update: just noticed that (for some reason I can't fathom) you don't want to ban modifying operations - you just want to ban SOME modifying operations. Very strange, but it's still the same solution. Whatever operations you want to allow, "open them up" in the interface:
public interface IReadOnlyDictionary<K, V> : IEnumerable<KeyValuePair<K, V>>
{
V this[K index] { get; set; }
}
Of course, that's the wrong name for the interface now... why on earth would you want to ban adding via Add but not ban it via the indexer? (The indexer can be used to add items, just as the Add method can.)
Update
From your comment I think you mean that you want to allow assignment to the value of an existing key/value pair, but disallow assignment to a previously unknown key. Obviously as keys are specified at runtime by strings, there's no way to catch that at compile time. So you may as well go for runtime checking:
public class FixedSizeDictionaryWrapper<TKey, TValue> : IDictionary<TKey, TValue>
{
IDictionary<TKey, TValue> _realDictionary;
public FixedSizeDictionaryWrapper(IDictionary<TKey, TValue> realDictionary)
{
_realDictionary = realDictionary;
}
public TValue this[TKey key]
{
get { return _realDictionary[key]; }
set
{
if (!_realDictionary.Contains(key))
throw new InvalidOperationException();
_realDictionary[key] = value;
}
}
// Implement Add so it always throws InvalidOperationException
// implement all other dictionary methods to forward onto _realDictionary
}
Any time you have an ordinary dictionary and you want to hand it to some method that you don't trust to update the existing values, wrap it in one of these.