Ответ 1
Если вы хотите запретить операции модификации во время компиляции, вам нужно безопасное решение.
Объявить интерфейс для общедоступных операций. Используйте этот интерфейс как тип свойства.
public interface IReadOnlyList<T>
{
T this[int index] { get; }
int Count { get; }
}
Затем объявите класс, который реализует этот интерфейс и наследует от стандартного класса коллекции.
public class SafeList<T> : List<T>, IReadOnlyList<T> { }
Предполагая, что вы правильно определяете определение интерфейса, вам не нужно ничего реализовывать вручную, поскольку базовый класс уже предоставляет реализации.
Используйте этот производный класс как тип поля, в котором хранится значение свойства.
public class A
{
private SafeList<string> _list = new SafeList<string>();
public IReadOnlyList<string>
{
get { return _list; }
}
}
В классе A вы можете напрямую использовать _list
и, следовательно, изменять содержимое. Клиенты класса A смогут использовать только подмножество операций, доступных через IReadOnlyList<T>
.
В вашем примере вы используете SortedList вместо List, поэтому, вероятно, интерфейс должен быть
public interface IReadOnlyDictionary<K, V> : IEnumerable<KeyValuePair<K, V>>
{
V this[K index] { get; }
}
Я также наследовал IEnumerable, который в любом случае является readonly, поэтому он абсолютно безопасен. Тогда безопасным классом будет:
public class SafeSortedList<K, V> : SortedList<K, V>, IReadOnlyDictionary<K, V> { }
Но в остальном это та же идея.
Обновление: только что заметили, что (по какой-то причине я не могу понять) вы не хотите запрещать модификацию операций - вы просто хотите запретить некоторые модифицирующие операции. Очень странно, но это все равно такое же решение. Независимо от того, какие операции вы хотите разрешить, "откройте их" в интерфейсе:
public interface IReadOnlyDictionary<K, V> : IEnumerable<KeyValuePair<K, V>>
{
V this[K index] { get; set; }
}
Конечно, что неправильное имя для интерфейса теперь... почему бы вам не хотеть запретить добавлять через Add, но не запрещать его с помощью индексатора? (Индексатор можно использовать для добавления элементов, как это может сделать метод Add.)
Обновление
Из вашего комментария, я думаю, вы имеете в виду, что хотите разрешить присвоение значению существующей пары ключ/значение, но запретить присвоение ранее неизвестному ключу. Очевидно, что, поскольку ключи указаны во время выполнения по строкам, нет способа поймать это во время компиляции. Таким образом, вы можете также проверить проверку выполнения:
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
}
В любое время, когда у вас есть обычный словарь, и вы хотите передать его некоторому методу, которому не доверяете, чтобы обновить существующие значения, оберните его одним из них.