Есть ли у С# способ дать мне неизменный словарь?
Есть ли что-либо, встроенное в основные библиотеки С#, которые могут дать мне неизменный словарь?
Что-то в строках Java:
Collections.unmodifiableMap(myMap);
И просто для того, чтобы уточнить, я не хочу останавливать изменение ключей/ценностей, а именно структуру словаря. Я хочу что-то, что не удастся быстро и громко, если какой-либо из методов IDictionary mutator вызывается (Add, Remove, Clear
).
Ответы
Ответ 1
Нет, но обертка довольно тривиальная:
public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
IDictionary<TKey, TValue> _dict;
public ReadOnlyDictionary(IDictionary<TKey, TValue> backingDict)
{
_dict = backingDict;
}
public void Add(TKey key, TValue value)
{
throw new InvalidOperationException();
}
public bool ContainsKey(TKey key)
{
return _dict.ContainsKey(key);
}
public ICollection<TKey> Keys
{
get { return _dict.Keys; }
}
public bool Remove(TKey key)
{
throw new InvalidOperationException();
}
public bool TryGetValue(TKey key, out TValue value)
{
return _dict.TryGetValue(key, out value);
}
public ICollection<TValue> Values
{
get { return _dict.Values; }
}
public TValue this[TKey key]
{
get { return _dict[key]; }
set { throw new InvalidOperationException(); }
}
public void Add(KeyValuePair<TKey, TValue> item)
{
throw new InvalidOperationException();
}
public void Clear()
{
throw new InvalidOperationException();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return _dict.Contains(item);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
_dict.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _dict.Count; }
}
public bool IsReadOnly
{
get { return true; }
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
throw new InvalidOperationException();
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dict.GetEnumerator();
}
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator()
{
return ((System.Collections.IEnumerable)_dict).GetEnumerator();
}
}
Очевидно, вы можете изменить этот установщик [] выше, если хотите разрешить изменение значений.
Ответ 2
Насколько я знаю, нет. Но может быть, вы можете скопировать некоторый код (и узнать много) из этих статей:
Неизменность в С# Часть первая: Виды неизменности
Неизменность в С# Часть вторая: простой неизменяемый стек
Неизменность в С# Часть третья: Ковариантный неизменяемый стек
Неизменность в С# Часть четвертая: неизменяемая очередь
Неизменность в С# Часть 6: Простой двоичный файл
Неизменность в С# Part Seven: больше на двоичных деревьях
Неизменность в С# Часть восьмая: еще больше на двоичных деревьях
Неизменность в С# Part Nine: Academic? Плюс моя реализация дерева AVL
Неизменность в С# Часть 10: очередь с двойным завершением
Неизменность в С# Part Eleven: рабочая двухсторонняя очередь
Ответ 3
С выпуском .NET 4.5 существует новый класс ReadOnlyDictionary. Вы просто передаете IDictionary
конструктору для создания неизменяемого словаря.
Здесь - полезный метод расширения, который можно использовать для упрощения создания словаря для чтения.
Ответ 4
Я так не думаю. Существует способ создания списка только для чтения и только для чтения, но я не думаю, что есть встроенный словарь только для чтения. System.ServiceModel имеет реализацию ReadOnlyDictinoary, но ее внутреннюю. Вероятно, было бы непросто скопировать его, используя Reflector, или просто создать свой собственный с нуля. Он в основном обертывает словарь и выдает, когда вызывается мутатор.
Ответ 5
Добавляя к ответ dbkk, я хотел иметь возможность использовать инициализатор объекта при первом создании моего ReadOnlyDictionary. Я внесла следующие изменения:
private readonly int _finalCount;
/// <summary>
/// Takes a count of how many key-value pairs should be allowed.
/// Dictionary can be modified to add up to that many pairs, but no
/// pair can be modified or removed after it is added. Intended to be
/// used with an object initializer.
/// </summary>
/// <param name="count"></param>
public ReadOnlyDictionary(int count)
{
_dict = new SortedDictionary<TKey, TValue>();
_finalCount = count;
}
/// <summary>
/// To allow object initializers, this will allow the dictionary to be
/// added onto up to a certain number, specifically the count set in
/// one of the constructors.
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void Add(TKey key, TValue value)
{
if (_dict.Keys.Count < _finalCount)
{
_dict.Add(key, value);
}
else
{
throw new InvalidOperationException(
"Cannot add pair <" + key + ", " + value + "> because " +
"maximum final count " + _finalCount + " has been reached"
);
}
}
Теперь я могу использовать класс следующим образом:
ReadOnlyDictionary<string, string> Fields =
new ReadOnlyDictionary<string, string>(2)
{
{"hey", "now"},
{"you", "there"}
};
Ответ 6
Библиотека с открытым исходным кодом PowerCollections включает в себя оболочку словаря только для чтения (а также оболочки только для чтения для почти всего остального), доступный с помощью статического метода ReadOnly()
класса Algorithms
.
Ответ 7
Один способ обхода может быть, бросить новый список KeyValuePair из Словаря, чтобы сохранить оригинальную немодифицированную.
var dict = new Dictionary<string, string>();
dict.Add("Hello", "World");
dict.Add("The", "Quick");
dict.Add("Brown", "Fox");
var dictCopy = dict.Select(
item => new KeyValuePair<string, string>(item.Key, item.Value));
// returns dictCopy;
Таким образом, исходный словарь не будет изменен.
Ответ 8
"Из коробки" нет способа сделать это. Вы можете создать его, выведя свой собственный класс Dictionary и внедряя необходимые ограничения.
Ответ 9
Я нашел реализацию Inmutable (не READONLY) реализации AVLTree для С# здесь.
Дерево AVL имеет логарифмическую (не постоянную) стоимость для каждой операции, но неподвижно.
http://csharpfeeds.com/post/7512/Immutability_in_Csharp_Part_Nine_Academic_Plus_my_AVL_tree_implementation.aspx
Ответ 10
Так как Linq существует общий интерфейс ILookup.
Подробнее в MSDN.
Следовательно, Чтобы просто получить неизменный словарь, вы можете позвонить:
using System.Linq;
// (...)
var dictionary = new Dictionary<string, object>();
// (...)
var read_only = dictionary.ToLookup(kv => kv.Key, kv => kv.Value);
Ответ 11
Вы можете попробовать что-то вроде этого:
private readonly Dictionary<string, string> _someDictionary;
public IEnumerable<KeyValuePair<string, string>> SomeDictionary
{
get { return _someDictionary; }
}
Это устранит проблему изменчивости в пользу того, чтобы ваш вызывающий абонент либо преобразовал его в свой словарь:
foo.SomeDictionary.ToDictionary(kvp => kvp.Key);
... или используйте операцию сравнения на ключе, а не индексный поиск, например:
foo.SomeDictionary.First(kvp => kvp.Key == "SomeKey");
Ответ 12
В общем, гораздо лучше не пропустить ни одного словаря в первую очередь (если вы НЕ ИМЕЕТЕ).
Вместо этого создайте объект-домен с интерфейсом, который не предлагает каких-либо методов, изменяющих словарь (который он обертывает). Вместо этого предлагается требуемый LookUp-метод, который извлекает элемент из словаря по ключу (бонус это упрощает использование, чем словарь).
public interface IMyDomainObjectDictionary
{
IMyDomainObject GetMyDomainObject(string key);
}
internal class MyDomainObjectDictionary : IMyDomainObjectDictionary
{
public IDictionary<string, IMyDomainObject> _myDictionary { get; set; }
public IMyDomainObject GetMyDomainObject(string key) {.._myDictionary .TryGetValue..etc...};
}
Ответ 13
Там также есть другая альтернатива, как я описал в:
http://www.softwarerockstar.com/2010/10/readonlydictionary-tkey-tvalue/
По существу это подкласс ReadOnlyCollection > , который выполняет работу более элегантно. Элегантный в том смысле, что у него есть поддержка времени компиляции для создания словаря только для чтения, а не для исключения исключений из методов, которые изменяют элементы внутри него.