Сделать словарь доступным только для чтения в C#

У меня есть Dictionary<string, List<string>> и хотел бы выставить участника только для чтения. Я вижу, что могу вернуть его как IReadOnlyDictionary<string, List<string>>, но я не могу понять, как вернуть его как IReadOnlyDictionary<string, IReadOnlyList<string>>,

Есть ли способ сделать это? В C++ я бы просто использовал const, но в C# его нет.

Обратите внимание, что просто с помощью IReadOnlyDictionary не помогает в этом случае, потому что я хочу, чтобы значения были только для чтения. Похоже, единственный способ сделать это - создать еще один IReadOnlyDictionary и добавить к ним IReadOnlyList.

Другой вариант, которым я не был бы в восторге, - это создать оболочку, которая реализует интерфейс IReadOnlyDictionary>, и сделать так, чтобы она содержала копию исходного экземпляра, но это кажется излишним.

7 ответов

Это было бы так же просто, как привести целую словарную ссылку на IReadOnlyDictionary<string, IReadOnlyList<string>> так как Dictionary<TKey, TValue> инвентарь IReadOnlyDictionary<TKey, TValue>,

Кстати, вы не можете сделать это, потому что вы хотите List<string> значения как IReadOnlyList<string>,

Итак, вам нужно что-то вроде этого:

var readOnlyDict = (IReadOnlyDictionary<string, IReadOnlyList<string>>)dict.ToDictionary(pair => pair.Key, pair => pair.Value.AsReadOnly());

Неизменные словари

Это всего лишь предложение, но если вы ищете неизменные словари, добавьте System.Collections.Immutable Пакет NuGet для вашего решения, и вы сможете использовать их:

// ImmutableDictionary<string, ImmutableList<string>>
var immutableDict = dict.ToImmutableDictionary(pair => pair.Key, pair => pair.Value.ToImmutableList());

Узнайте больше о неизменных коллекциях здесь.

Учитывая тот факт, что вы специально ищете только для чтения Dictionary<string, List<string>>вы в основном ищете именно Lookup.

Объект Dictionary имеет ToLookup() расширение.

У меня та же проблема. Я решил это следующим образом.

List<string> list = new List<string>();

Dictionary<string, IReadOnlyCollection<string>> dic = new Dictionary<string, IReadOnlyCollection<string>>();

IReadOnlyDictionary<string, IReadOnlyCollection<string>> dicRo = new ReadOnlyDictionary<string, IReadOnlyCollection<string>>(dic);

 list.Add("Test1");

 dic["T"] = list.AsReadOnly();

 ist.Add("Test2");

Это положительный эффект, что вы

  • все еще может добавлять элементы в список
  • все еще может добавлять элементы в словарь
  • не может редактировать ReadOnlyDictionary
  • не может редактировать ReadOnlyCollection
  • не могу передать это в словарь
  • не могу добавить его в список
  • всегда обновляйте ваш ReadOnlyDictionary

Может это кому-то поможет.

Сначала вам нужно создать новый словарь с желаемыми типами контента:

var dicWithReadOnlyList = dic.ToDictionary(
    kv => kv.Key,
    kv => kv.Value.AsReadOnly());

Тогда вы можете просто вернуть новый словарь, так как IReadOnlyDictionary это супертип Dictionary,


Зачем тебе это нужно? Так как Dictionary<T, A> не супертип Dictionary<T, B>даже если A это супертип B, Зачем? Рассмотрим следующий пример:

var dic = new Dictionary<T, B>();
Dictionary<T, A> dic2 = dic;      // Imagine this were possible...

dic2.Add(someT, someA);           // ...then we'd have a type violation here, since
                                  // dic2 = dic requires some B as the value.

Другими словами, TValue в Dictionary не является ковариантным. С объектно-ориентированной точки зрения ковариация должна быть возможной в версии словаря только для чтения, но в платформе.NET существуют устаревшие проблемы, которые этому препятствуют (подробности см. В части, начинающейся с "ОБНОВЛЕНИЕ" в этом вопросе).).

Не отвечая напрямую на вопрос, но .NET 8 представляет FrozenDictionary.

НовыйSystem.Collections.Frozenпространство имен включает типы коллекцийFrozenDictionary<TKey,TValue>иFrozenSet<T>. Эти типы не позволяют вносить изменения в ключи и значения после создания коллекции. Это требование позволяет ускорить операции чтения (например,TryGetValue()). Эти типы особенно полезны для коллекций, которые заполняются при первом использовании, а затем сохраняются в течение длительного срока службы, например:

Если вы хотите вернуть словарь только для чтения, но при этом можете изменять словарь и список в вашем классе, вы можете использовать приведение, чтобы получить тип списка.

Этот пример немного надуманный, но показывает, как он может работать.

public class MyClass
{
    Dictionary<string, IReadOnlyList<string>> _dictionary;
    public IReadOnlyDictionary<string, IReadOnlyList<string>> Dictionary { get { return _dictionary; } }

    public MyClass()
    {
        _dictionary = new Dictionary<string, IReadOnlyList<string>>();
    }

    public void AddItem(string item)
    {
        IReadOnlyList<string> readOnlyList = null;
        List<string> list = null;
        if (!_dictionary.TryGetValue(item, out readOnlyList))
        {
            list = new List<string>();
            _dictionary.Add(item, list);
        }
        else
            list = readOnlyList as List<string>;
        list.Add(item);
    }
}

Если ваша цель состоит в том, чтобы свойство было неизменным, тогда использование ReadOnlyDictionary будет лучшим вариантом.

https://msdn.microsoft.com/en-us/library/acdd6hb7.aspx

Вы можете использовать это, чтобы выставить объект как только для чтения.

Вы также можете использовать свойства get; задавать; и только позволить получить быть публичным.

Но ответ Матиаса кажется более подходящим.

Другие вопросы по тегам