Тип словаря С# с уникальными ключами и значениями
Мне было интересно, был ли встроенный тип в С#, похожий на "Словарь", но где оба TKey и TValue должны были быть уникальными.
Например:
d.Add(1, "1");
d.Add(2, "1"); // This would not be OK because "1" has already been used as a value.
Я знаю, что это своего рода экзотика, но кажется, что, поскольку в BCL существует около миллиарда типов коллекций, они могут существовать. Любые идеи?
Ответы
Ответ 1
Как насчет словаря и HashSet/вторичного обратного словаря - он решит проблему и будет работать лучше, чем проверки на одном словаре.
Что-то вроде этого, завернутое как класс:
HashSet<string> secondary = new HashSet<string>(/*StringComparer.InvariantCultureIgnoreCase*/);
Dictionary<int, string>dictionary = new Dictionary<int, string>();
object syncer = new object();
public override void Add(int key, string value)
{
lock(syncer)
{
if(dictionary.ContainsKey(key))
{
throw new Exception("Key already exists");
}
if(secondary.Add(value)
{
throw new Exception("Value already exists");
}
dictionary.Add(key, value);
}
}
Ответ 2
Для внутренних пупок я написал a BiDictionary
. Это не пуленепробиваемый, я не выставляю его пользователю, поэтому он отлично работает для меня. Это позволяет мне получить либо ключ, сколько мне нужно.
KeyPair<,>
необходимо, чтобы реализовать метод IEnumerable<,>
и, следовательно, Add
, чтобы мы могли использовать инициализатор объекта.
internal class KeyPair<TKey1, TKey2>
{
public TKey1 Key1 { get; set; }
public TKey2 Key2 { get; set; }
}
Это основной класс как динамический объект, так что мы можем использовать его при извлечении значений:
internal class BiDictionary<TKey1, TKey2> : DynamicObject, IEnumerable<KeyPair<TKey1, TKey2>>
{
private readonly Dictionary<TKey1, TKey2> _K1K2 = new Dictionary<TKey1, TKey2>();
private readonly Dictionary<TKey2, TKey1> _K2K1 = new Dictionary<TKey2, TKey1>();
private readonly string _key1Name;
private readonly string _key2Name;
public BiDictionary(string key1Name, string key2Name)
{
_key1Name = key1Name;
_key2Name = key2Name;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (binder.Name == _key1Name)
{
result = _K1K2;
return true;
}
if (binder.Name == _key2Name)
{
result = _K2K1;
return true;
}
result = null;
return false;
}
public void Add(TKey1 key1, TKey2 key2)
{
_K1K2.Add(key1, key2);
_K2K1.Add(key2, key1);
}
public IEnumerator<KeyPair<TKey1, TKey2>> GetEnumerator()
{
return _K1K2.Zip(_K2K1, (d1, d2) => new KeyPair<TKey1, TKey2>
{
Key1 = d1.Key,
Key2 = d2.Key
}).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Пример:
dynamic bidic = new BiDictionary<string, string>("Key1", "Key2")
{
{ "foo", "bar" },
{ "baz", "qux" }
};
var bar = bidic.Key1["foo"];
var foo = bidic.Key2["bar"];
Они могут выйти из синхронизации, если вы измените ни один из словарей вне. Для этой цели я использую ObservableDictionary
, чтобы я мог обновить другую, если кто-то изменился, но для простоты я удалил эту часть кода, чтобы просто проигнорировать основную логику.
Ответ 3
Существует проект здесь, который имеет такой тип. Он называется PairDictionary, и он работает очень хорошо. Не лучший ответ, но для тех, кому нужен этот пользовательский класс.
Ответ 4
Я решил эту проблему, сохранив данные как Dictionary<TKey, HashSet<TValue>>
.
Вы можете заменить HashSet другим Словарем, если вы хотите иметь 2 первичных ключа.
Dictionary<int, HashSet<int>> _myUniquePairOfIntegerKeys;
// OR
Dictionary<string, Dictionary<string, bool>> _myUniquePairOfStringKeysWithABooleanValue;