В .NET доступен общий словарь только для чтения? - PullRequest
185 голосов
/ 24 марта 2009

Я возвращаю ссылку на словарь в своем свойстве только для чтения. Как я могу запретить потребителям изменять мои данные? Если бы это был IList, я мог бы просто вернуть его AsReadOnly. Есть ли что-то подобное, что я могу сделать со словарем?

Private _mydictionary As Dictionary(Of String, String)
Public ReadOnly Property MyDictionary() As Dictionary(Of String, String)
    Get
        Return _mydictionary
    End Get
End Property

Ответы [ 14 ]

1 голос
/ 24 марта 2009

Я не думаю, что есть простой способ сделать это ... если ваш словарь является частью пользовательского класса, вы можете достичь этого с помощью индексатора:

public class MyClass
{
  private Dictionary<string, string> _myDictionary;

  public string this[string index]
  {
    get { return _myDictionary[index]; }
  }
}
1 голос
/ 24 марта 2009

Вы можете создать класс, который реализует только частичную реализацию словаря и скрывает все функции добавления / удаления / установки.

Используйте внутренний словарь, к которому внешний класс передает все запросы.

Однако, поскольку ваш словарь, скорее всего, содержит ссылочные типы, вы не сможете остановить пользователя от установки значений для классов, содержащихся в словаре (если только эти классы не предназначены только для чтения)

0 голосов
/ 13 февраля 2014

Это плохое решение, см. Внизу.

Для тех, кто все еще использует .NET 4.0 или более раннюю версию, у меня есть класс, который работает так же, как в принятом ответе, но он намного короче. Он расширяет существующий объект Dictionary, переопределяя (фактически скрывая) определенные члены, чтобы они вызывали исключение при вызове.

Если вызывающая сторона пытается вызвать Add, Remove или какую-либо другую мутирующую операцию, имеющуюся во встроенном словаре, компилятор выдаст ошибку. Я использую устаревшие атрибуты, чтобы вызвать эти ошибки компилятора. Таким образом, вы можете заменить Dictionary на этот ReadOnlyDictionary и сразу увидеть, где могут возникнуть какие-либо проблемы, без необходимости запуска приложения и ожидания исключений во время выполнения.

Взгляните:

public class ReadOnlyException : Exception
{
}

public class ReadOnlyDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
    public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
        : base(dictionary) { }

    public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
        : base(dictionary, comparer) { }

    //The following four constructors don't make sense for a read-only dictionary

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary() { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary(IEqualityComparer<TKey> comparer) { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary(int capacity) { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary(int capacity, IEqualityComparer<TKey> comparer) { throw new ReadOnlyException(); }


    //Use hiding to override the behavior of the following four members
    public new TValue this[TKey key]
    {
        get { return base[key]; }
        //The lack of a set accessor hides the Dictionary.this[] setter
    }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public new void Add(TKey key, TValue value) { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public new void Clear() { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public new bool Remove(TKey key) { throw new ReadOnlyException(); }
}

В этом решении есть проблема, указанная @supercat, показанной здесь:

var dict = new Dictionary<int, string>
{
    { 1, "one" },
    { 2, "two" },
    { 3, "three" },
};

var rodict = new ReadOnlyDictionary<int, string>(dict);
var rwdict = rodict as Dictionary<int, string>;
rwdict.Add(4, "four");

foreach (var item in rodict)
{
    Console.WriteLine("{0}, {1}", item.Key, item.Value);
}

Вместо того, чтобы выдавать ошибку во время компиляции, как я ожидал, или исключение времени выполнения, как я надеялся, этот код выполняется без ошибок. Он печатает четыре номера. Это делает мой ReadOnlyDictionary ReadWriteDictionary.

0 голосов
/ 24 марта 2009
public IEnumerable<KeyValuePair<string, string>> MyDictionary()
{
    foreach(KeyValuePair<string, string> item in _mydictionary)
        yield return item;
}
...