Я знаю, что в BCL нет ни одного, но может ли кто-нибудь указать мне на хороший источник с открытым исходным кодом?Многоязычный словарь в C#?
By Multi Я имею в виду 2 ключа. ;-)
Я знаю, что в BCL нет ни одного, но может ли кто-нибудь указать мне на хороший источник с открытым исходным кодом?Многоязычный словарь в C#?
By Multi Я имею в виду 2 ключа. ;-)
В настоящее время я просто объединяю ключи в одну строку в качестве обходного пути. Конечно, это не будет работать на нестрочных клавишах. Хотелось бы также узнать ответ.
Почему бы и нет .ToString() нестрочные ключи? – n8wrl
Потому что для большинства классов .ToSting() будет одинаковым для всех значений (т. Е. Названия типов). Не все типы имеют допустимое строковое представление и т. Д. И т. Д. –
Да, но вы можете обычно переопределять ToString в этих классах. – Badaro
Я использую Tuple
в качестве ключей в Dictionary
.
public class Tuple<T1, T2> {
public T1 Item1 { get; private set; }
public T2 Item2 { get; private set; }
// implementation details
}
Обязательно переопределить Equals
и GetHashCode
и определить operator!=
и operator==
в зависимости от обстоятельств. Вы можете развернуть Tuple
для хранения большего количества предметов по мере необходимости. .NET 4.0 будет включать встроенный Tuple
.
Отметьте это сообщение в блоге о тестах производительности между несколькими «ключевыми» реализациями словаря [здесь] (http://www.fyslexicduck.com/2011/05/performance-tests-between-multiple.html) –
@AronW Насколько я могу сказать, сообщение в блоге никогда не использует «Tuple» в качестве ключа? – flindeberg
Кортеж будет (есть) в .Net 4.0 До тех пор, вы можете также использовать
Dictionary<key1, Dictionary<key2, TypeObject>>
или создания пользовательского класса коллекции, чтобы представить это ...
public class TwoKeyDictionary<K1, K2, T>:
Dictionary<K1, Dictionary<K2, T>> { }
или с тремя ключами ...
public class ThreeKeyDictionary<K1, K2, K3, T> :
Dictionary<K1, Dictionary<K2, Dictionary<K3, T>>> { }
Блестящий ответ - это отлично работало для моих нужд. – HanClinto
Взгляните на Wintellect-х PowerCollections (CodePlex download). Я думаю, что их MultiDictionary делает что-то подобное.
Это словарь словарей, поэтому у вас есть 2 ключа для доступа к каждому объекту, ключ для основного словаря, чтобы получить требуемый дополнительный словарь, а затем второй ключ для дополнительного словаря, чтобы получить необходимый вам предмет. Это то, что вы имели ввиду?
Их 'MultiDictionary <,>' позволяет назначать несколько значений одному ключу, а не иметь несколько ключей в этом смысле. – pbalaga
, но вопрос OP такой расплывчатый, какой он может получить. Следовательно, +1 для другого брака. – nawfal
@pbalaga - это правильно, PowerCollections не позволяет вам иметь два ключа (из-за этого занижены) –
Я думаю, вам понадобится класс Tuple2. Убедитесь, что это GetHashCode() и Equals() основаны на двух содержащихся элементах.
См Tuples in C#
Есть ли что-нибудь случилось с
new Dictionary<KeyValuePair<object, object>, object>?
Есть ли встроенная пара
@MichaelDonohue: с .NET 4.0 существует класс ['Tuple'] (http://msdn.microsoft.com/en-us/library/system.tuple.aspx). –
Да, что-то не так: KVP использует [ValueType.GetHashCode] (http://msdn.microsoft.com/en-us/library/system.valuetype.gethashcode.aspx) - «Если вы вызываете метод GetHashCode производного типа , возвращаемое значение вряд ли подходит для использования в качестве ключа в хеш-таблице ». KVP является совершенно неуместным выбором для ключа словаря и может привести к множеству столкновений. – bacar
Я уже использовал кортежи как jason в его ответе. Тем не менее, я предлагаю вам просто определить кортеж в качестве структуры:
public struct Tuple<T1, T2> {
public readonly T1 Item1;
public readonly T2 Item2;
public Tuple(T1 item1, T2 item2) { Item1 = item1; Item2 = item2;}
}
public static class Tuple { // for type-inference goodness.
public static Tuple<T1,T2> Create<T1,T2>(T1 item1, T2 item2) {
return new Tuple<T1,T2>(item1, item2);
}
}
Вы неизменность, .GetHashcode
и .Equals
бесплатно, что (в то время как вы ждете C# 4.0) для хорошо н просто ...
Один предупреждение однако: реализация по умолчанию GetHashcode
(иногда) only considers the first field поэтому убедитесь, чтобы сделать первое поле самого разборчивого или реализовать GetHashcode
себя (например, с использованием FieldwiseHasher.Hash(this)
от ValueUtils), в противном случае вы, вероятно, столкнуться с проблемами масштабируемости.
Кроме того, вы избегаете ошибок, которые, как правило, усложняют вопросы (и если вы действительно хотите получить нули, вы просто делаете свой Tuple<>
допустимым к нулю). Немного offtopic, я единственный, кто раздражен на уровне фреймового уровня поддержки непустых ссылок?Я работаю над большим проектом, а иногда нулевыми ползучами, где-то это действительно не должно быть - и hey presto, вы получаете исключение с нулевой ссылкой, но с трассировкой стека, указывающей на первое использование ссылки, а не на действительно ошибочный код ,
Конечно, .NET 4.0 довольно устарел; большинство из нас могут просто использовать кортеж .NET 4.0.
Edit:, чтобы обойти бедную GetHashCode
реализации, .NET предоставляет для структур, которые я написал ValueUtils, который также позволяет использовать реальные имена для ключей многопрофильных; это означает, что вы могли бы написать что-то вроде:
sealed class MyValueObject : ValueObject<MyValueObject> {
public DayOfWeek day;
public string NamedPart;
//properties work fine too
}
... который, мы надеемся, делает его легче иметь человек считываемые имена для данных с семантикой значений, по крайней мере до some future version of C# implements proper tuples with named members; надеюсь, с достойными хэш-кодами ;-).
Хорошая реклама по сравнению с решением типа value для кортежей в .NET: http://msdn.microsoft.com/en-us/magazine/dd942829.aspx#id0400060 – jason
Действительно (после прочтения статьи), если вы используете default struct-implied .Equals и реализация .GetHashcode, кортеж с двойником, который является NaN, будет равен самому себе, хотя double.NaN! = double.NaN ... Я могу жить с этим. –
В этой статье говорится, что они использовали ссылочный тип, так как не хотели вызывать путаницу из-за несогласованности семантики между типами структуры/ссылки, но я не вижу никаких аргументов относительно того, почему ссылочный тип был бы предпочтительнее; просто, что вы хотите, чтобы все кортежи были одного типа. –
Я искал для Google: http://www.codeproject.com/KB/recipes/multikey-dictionary.aspx. Я полагаю, что это основная функция по сравнению с использованием структуры, содержащей 2 ключа в обычном словаре, так это то, что вы можете позже ссылаться на один из ключей вместо того, чтобы иметь 2 ключа.
Не могли бы вы использовать Dictionary<TKey1,Dictionary<TKey2,TValue>>
?
Можно даже создать подкласс этого:
public class DualKeyDictionary<TKey1,TKey2,TValue> : Dictionary<TKey1,Dictionary<TKey2,TValue>>
EDIT: Это теперь дубликатом ответ. Он также ограничен в своей практичности. Хотя он «работает» и предоставляет возможность кода dict[key1][key2]
, есть много «обходных решений», чтобы заставить его «просто работать».
ОДНАКО: Только для пинков, можно реализовать словарь, тем не менее, но на данный момент он получает немного Verbose:
public class DualKeyDictionary<TKey1, TKey2, TValue> : Dictionary<TKey1, Dictionary<TKey2, TValue>> , IDictionary< object[], TValue >
{
#region IDictionary<object[],TValue> Members
void IDictionary<object[], TValue>.Add(object[] key, TValue value)
{
if (key == null || key.Length != 2)
throw new ArgumentException("Invalid Key");
TKey1 key1 = key[0] as TKey1;
TKey2 key2 = key[1] as TKey2;
if (!ContainsKey(key1))
Add(key1, new Dictionary<TKey2, TValue>());
this[key1][key2] = value;
}
bool IDictionary<object[], TValue>.ContainsKey(object[] key)
{
if (key == null || key.Length != 2)
throw new ArgumentException("Invalid Key");
TKey1 key1 = key[0] as TKey1;
TKey2 key2 = key[1] as TKey2;
if (!ContainsKey(key1))
return false;
if (!this[key1].ContainsKey(key2))
return false;
return true;
}
Вот конкретизированы пример класса пар, который может быть использован в качестве ключ к словарю.
public class Pair<T1, T2> {
public T1 Left { get; private set; }
public T2 Right { get; private set; }
public Pair(T1 t1, T2 t2) {
Left=t1;
Right=t2;
}
public override bool Equals(object obj) {
if(ReferenceEquals(null, obj)) return false;
if(ReferenceEquals(this, obj)) return true;
if(obj.GetType()!=typeof(Pair<T1, T2>)) return false;
return Equals((Pair<T1, T2>)obj);
}
public bool Equals(Pair<T1, T2> obj) {
if(ReferenceEquals(null, obj)) return false;
if(ReferenceEquals(this, obj)) return true;
return Equals(obj.Left, Left) && Equals(obj.Right, Right);
}
public override int GetHashCode() {
unchecked {
return (Left.GetHashCode()*397)^Right.GetHashCode();
}
}
}
Я часто использую это, потому что это коротко и обеспечивает синтаксический сахар, мне нужно ...
public class MultiKeyDictionary<T1, T2, T3> : Dictionary<T1, Dictionary<T2, T3>>
{
new public Dictionary<T2, T3> this[T1 key]
{
get
{
if (!ContainsKey(key))
Add(key, new Dictionary<T2, T3>());
Dictionary<T2, T3> returnObj;
TryGetValue(key, out returnObj);
return returnObj;
}
}
}
Чтобы использовать его:
dict[cat][fish] = 9000;
где "Cat" ключ Безразлично» так и должно существовать.
Я также должен отметить, что произвольно создавать дополнительные вложения с другим классом
Что такое хороший способ проверить, существуют ли ключи [cat] [fish] или [cat] [mouse]? –
@goku_da_master 'dict [cat] .ContainsKey (mouse)' где, если cat не существует, вы всегда становитесь ложным, потому что он обновляется тогда. Если вы будете использовать его таким образом, я бы сравнил его, хотя это не будет очень оптимизированным способом сделать это. – max
Я написал и использовал это с успехом.
public class MultiKeyDictionary<K1, K2, V> : Dictionary<K1, Dictionary<K2, V>> {
public V this[K1 key1, K2 key2] {
get {
if (!ContainsKey(key1) || !this[key1].ContainsKey(key2))
throw new ArgumentOutOfRangeException();
return base[key1][key2];
}
set {
if (!ContainsKey(key1))
this[key1] = new Dictionary<K2, V>();
this[key1][key2] = value;
}
}
public void Add(K1 key1, K2 key2, V value) {
if (!ContainsKey(key1))
this[key1] = new Dictionary<K2, V>();
this[key1][key2] = value;
}
public bool ContainsKey(K1 key1, K2 key2) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2);
}
public new IEnumerable<V> Values {
get {
return from baseDict in base.Values
from baseKey in baseDict.Keys
select baseDict[baseKey];
}
}
}
public class MultiKeyDictionary<K1, K2, K3, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, V>> {
public V this[K1 key1, K2 key2, K3 key3] {
get {
return ContainsKey(key1) ? this[key1][key2, key3] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, V>();
this[key1][key2, key3] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3);
}
}
public class MultiKeyDictionary<K1, K2, K3, K4, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, V>();
this[key1][key2, key3, key4] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4);
}
}
public class MultiKeyDictionary<K1, K2, K3, K4, K5, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, V>();
this[key1][key2, key3, key4, key5] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5);
}
}
public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, V>();
this[key1][key2, key3, key4, key5, key6] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6);
}
}
public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, V>();
this[key1][key2, key3, key4, key5, key6, key7] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7);
}
}
public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7, key8] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, V>();
this[key1][key2, key3, key4, key5, key6, key7, key8] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8);
}
}
public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, K9, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7, key8, key9] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, V>();
this[key1][key2, key3, key4, key5, key6, key7, key8, key9] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8, key9);
}
}
public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, K10 key10] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, V>();
this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, K10 key10) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8, key9, key10);
}
}
public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, K10 key10, K11 key11] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10, key11] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, V>();
this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10, key11] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, K10 key10, K11 key11) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8, key9, key10, key11);
}
}
Я написал расширение для ToMultiKeyDictionary() для этого ответа и [отправил его как ответ ниже] (https://stackoverflow.com/a/21270886/435383) – katbyte
Вот моя реализация. Я хотел что-то скрывать от реализации концепции Tuple.
public class TwoKeyDictionary<TKey1, TKey2, TValue> : Dictionary<TwoKey<TKey1, TKey2>, TValue>
{
public static TwoKey<TKey1, TKey2> Key(TKey1 key1, TKey2 key2)
{
return new TwoKey<TKey1, TKey2>(key1, key2);
}
public TValue this[TKey1 key1, TKey2 key2]
{
get { return this[Key(key1, key2)]; }
set { this[Key(key1, key2)] = value; }
}
public void Add(TKey1 key1, TKey2 key2, TValue value)
{
Add(Key(key1, key2), value);
}
public bool ContainsKey(TKey1 key1, TKey2 key2)
{
return ContainsKey(Key(key1, key2));
}
}
public class TwoKey<TKey1, TKey2> : Tuple<TKey1, TKey2>
{
public TwoKey(TKey1 item1, TKey2 item2) : base(item1, item2) { }
public override string ToString()
{
return string.Format("({0},{1})", Item1, Item2);
}
}
Это помогает держит использование выглядит как словарь
item.Add(1, "D", 5.6);
value = item[1, "D"];
как перебирать все? – MobileMon
Если кто-то ищет ToMultiKeyDictionary()
здесь является реализация, которая должна работать с большинством ответов здесь (на основе Herman's):
public static class Extensions_MultiKeyDictionary {
public static MultiKeyDictionary<K1, K2, V> ToMultiKeyDictionary<S, K1, K2, V>(this IEnumerable<S> items, Func<S, K1> key1, Func<S, K2> key2, Func<S, V> value) {
var dict = new MultiKeyDictionary<K1, K2, V>();
foreach (S i in items) {
dict.Add(key1(i), key2(i), value(i));
}
return dict;
}
public static MultiKeyDictionary<K1, K2, K3, V> ToMultiKeyDictionary<S, K1, K2, K3, V>(this IEnumerable<S> items, Func<S, K1> key1, Func<S, K2> key2, Func<S, K3> key3, Func<S, V> value) {
var dict = new MultiKeyDictionary<K1, K2, K3, V>();
foreach (S i in items) {
dict.Add(key1(i), key2(i), key3(i), value(i));
}
return dict;
}
}
Много хороших решений здесь, Что я здесь отсутствует является реализация на основе построения в Tuple
, поэтому я написал сам.
Поскольку он просто наследуется от Dictionary<Tuple<T1,T2>, T>
, вы всегда можете использовать оба способа.
var dict = new Dictionary<int, int, Row>();
var row = new Row();
dict.Add(1, 2, row);
dict.Add(Tuple.Create(1, 2, row));
dict.Add(new Tuple<int, int>(1, 2));
вот код.
public class Dictionary<TKey1,TKey2,TValue> : Dictionary<Tuple<TKey1, TKey2>, TValue>, IDictionary<Tuple<TKey1, TKey2>, TValue>
{
public TValue this[TKey1 key1, TKey2 key2]
{
get { return base[Tuple.Create(key1, key2)]; }
set { base[Tuple.Create(key1, key2)] = value; }
}
public void Add(TKey1 key1, TKey2 key2, TValue value)
{
base.Add(Tuple.Create(key1, key2), value);
}
public bool ContainsKey(TKey1 key1, TKey2 key2)
{
return base.ContainsKey(Tuple.Create(key1, key2));
}
}
Обратите внимание, что эта реализация зависит от Tuple.Equals() реализация сама по себе:
http://msdn.microsoft.com/en-us/library/dd270346(v=vs.110).aspx
Параметр OBJ считается равным текущему экземпляру при следующих условиях:
Простой и хорошо работает, спасибо. – 56ka
Очень приятное решение –
Вот еще один пример использования класса Tuple с Dictionary.
// Setup Dictionary
Dictionary<Tuple<string, string>, string> testDictionary = new Dictionary<Tuple<string, string>, string>
{
{new Tuple<string, string>("key1","key2"), "value1"},
{new Tuple<string, string>("key1","key3"), "value2"},
{new Tuple<string, string>("key2","key3"), "value3"}
};
//Query Dictionary
public string FindValue(string stuff1, string stuff2)
{
return testDictionary[Tuple.Create(stuff1, stuff2)];
}
Вам нужен ключ, который состоит из нескольких атрибутов или вы хотите, чтобы один и тот же ключ мог существовать более одного раза в одном словаре? Это разные. – Polaris878
http://stackoverflow.com/questions/689940/hashtable-with-multidimensional-key-in-c –
Возможно, вы захотите добавить пример использования, чтобы уточнить, что вы имеете в виду. –