Многоязычный словарь в С#?
Я знаю, что в BCL нет ни одного, но может ли кто-нибудь указать мне на хороший вариант с открытым исходным кодом?
По Multi Я имею в виду 2 клавиши.; -)
Ответы
Ответ 1
Я также использовал кортежи, как Джейсон в его ответе. Однако я предлагаю вам просто определить кортеж как структуру:
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
бесплатно, которые (пока вы ждете С# 4.0) приятно "n просто...
Однако одно предупреждение: реализация GetHashcode
по умолчанию (иногда) учитывает только первое поле, поэтому обязательно сделайте первое поле наиболее дискриминационным или реализуйте GetHashcode
самостоятельно (например, используя FieldwiseHasher.Hash(this)
из ValueUtils), иначе вы, скорее всего, в вопросах масштабируемости.
Кроме того, вы избегаете ошибок, которые, как правило, усложняют ситуацию (и если вы действительно хотите получить нули, вы просто делаете свой Tuple<>
допустимым с нулевой отметкой). Немного offtopic, я единственный, кто раздражен на уровне фреймового уровня поддержки непустых ссылок? Я работаю над большим проектом, а иногда нулевыми ползучами, где-то это действительно не должно быть - и hey presto, вы получаете исключение с нулевой отметкой, но с трассировкой стека, которая указывает на первое использование ссылки, а не на действительно ошибочный код,
Конечно,.NET 4.0 довольно устарел; большинство из нас может просто использовать кортеж.NET 4.0.
Изменить: для решения проблемы неудачной реализации GetHashCode
которую.NET предоставляет для структур, которые я написал ValueUtils, что также позволяет вам использовать настоящие имена для ваших многополюсных ключей; это означает, что вы можете написать что-то вроде:
sealed class MyValueObject : ValueObject<MyValueObject> {
public DayOfWeek day;
public string NamedPart;
//properties work fine too
}
... которые, как мы надеемся, упрощают получение понятных для человека имен для данных со значениями семантики, по крайней мере до тех пор, пока какая-то будущая версия С# не реализует правильные кортежи с именованными членами; надеюсь, с достойными хэш-кодами ;-).
Ответ 2
Я использую 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
.
Ответ 3
Кортежи будут (находятся) в .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>>> { }
Ответ 4
Много хороших решений здесь,
То, что мне не хватает, это реализация, основанная на построении типа 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 считается равным текущему экземпляру при следующих условиях:
- Это объект Tuple.
- Два его компонента имеют те же типы, что и текущий экземпляр.
- Его две компоненты равны двум компонентам текущего экземпляра. Равенство определяется по умолчанию для сравнения компонентов для каждого компонента.
Ответ 5
Я написал и использовал это с успехом.
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);
}
}
Ответ 6
В настоящее время я просто объединяю ключи в одну строку в качестве обходного пути. Конечно, это не будет работать на нестрочных клавишах. Хотелось бы узнать и ответ.
Ответ 7
Взгляните на Wintellect PowerCollections (Загрузка CodePlex). Я думаю, что их MultiDictionary делает что-то подобное.
Это словарь словарей, поэтому у вас есть 2 ключа для доступа к каждому объекту, ключ для основного словаря, чтобы получить нужный дополнительный словарь, а затем второй ключ для дополнительного словаря, чтобы получить нужный вам предмет. Это то, что вы имеете в виду?
Ответ 8
Что-то не так с
new Dictionary<KeyValuePair<object, object>, object>
?
Ответ 9
Я часто использую это, потому что он короткий и обеспечивает синтаксический сахар, который мне нужен...
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" также не должен существовать.
Ответ 10
Я искал эту страницу: http://www.codeproject.com/KB/recipes/multikey-dictionary.aspx. Я полагаю, что основная функция по сравнению с использованием структуры, содержащей 2 ключа в обычном словаре, состоит в том, что вы можете позже ссылаться на один из ключей вместо того, чтобы снабжать 2 ключами.
Ответ 11
Я думаю, вам понадобится класс Tuple2. Убедитесь, что GetHashCode() и Equals() основаны на двух содержащихся элементах.
Смотрите Кортежи в С#
Ответ 12
Не могли бы вы использовать Dictionary<TKey1,Dictionary<TKey2,TValue>>
?
Вы можете даже подклассифицировать это:
public class DualKeyDictionary<TKey1,TKey2,TValue> : Dictionary<TKey1,Dictionary<TKey2,TValue>>
РЕДАКТИРОВАТЬ: Теперь это дублированный ответ. Он также ограничен в своей практичности. Хотя он "работает" и обеспечивает возможность кода dict[key1][key2]
, существует множество "обходных решений", чтобы заставить его "просто работать".
ОДНАКО: Только для ударов, однако, можно реализовать словарь, но на данный момент он становится немного подробным:
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;
}
Ответ 13
Здесь приведен пример из пара-класса, который можно использовать в качестве ключа к словарю.
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();
}
}
}
Ответ 14
Если кто-то ищет 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;
}
}
Ответ 15
Вот моя реализация. Я хотел что-то скрывать реализацию концепции 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"];
Ответ 16
Вот еще один пример использования класса 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)];
}