Сделать словарь доступным только для чтения в 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; задавать; и только позволить получить быть публичным.
Но ответ Матиаса кажется более подходящим.