BCLにはないことは知っていますが、良いオープンソースの1つを誰かに指摘できますか?
マルチとは、2つのキーを意味します。 ;-)
彼の答えのジェイソン のようにタプルも使用しました。ただし、単にTupleを構造体として定義することをお勧めします。
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を待っている間に)Nice 'n simple ...です。
1つwarningただし、デフォルトのGetHashcode
実装(場合によっては) 最初のフィールドのみを考慮 ですので、必ず最初のフィールドをほとんどの場合、GetHashcode
を識別または実装します(たとえば、 ValueUtils のFieldwiseHasher.Hash(this)
を使用します)。そうしないと、スケーラビリティの問題が発生する可能性があります。
また、問題を複雑にする傾向があるヌルを避けることができます(そして、本当にヌルが必要な場合は、Tuple<>
をヌル可能にするだけです)。少しオフトピックですが、フレームワークレベルでの非null参照のサポート不足に悩まされているのは私だけですか?私は大規模なプロジェクトに取り組んでいますが、実際にはすべきではない場所でnullが忍び寄る場合があります-そして、実際にはnullreference例外が発生します-しかし、実際の障害のあるコードではなく、参照の最初の使用を指すスタックトレースがあります。
もちろん、.NET 4.0は今ではかなり古いものです。私たちのほとんどは、.NET 4.0のTupleを使用できます。
Edit:.NETが私が書いた構造体に提供する貧弱なGetHashCode
実装を回避する- ValueUtils 。これにより、マルチフィールドキーに実際の名前を使用することもできます。つまり、次のように記述できます。
sealed class MyValueObject : ValueObject<MyValueObject> {
public DayOfWeek day;
public string NamedPart;
//properties work fine too
}
...これは、少なくとも C#の将来のバージョンでは、名前付きメンバーを持つ適切なタプルを実装する ;うまくいけば、まともなハッシュコードで;-)。
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
が含まれます。
タプルは.Net 4.0に含まれます(それまで)。それまでは、
Dictionary<key1, Dictionary<key2, TypeObject>>
または、これを表すカスタムコレクションクラスを作成しています...
public class TwoKeyDictionary<K1, K2, T>:
Dictionary<K1, Dictionary<K2, T>> { }
または、3つのキーで...
public class ThreeKeyDictionary<K1, K2, K3, T> :
Dictionary<K1, Dictionary<K2, Dictionary<K3, T>>> { }
ここにある多くの良い解決策、私がここで見逃しているのは、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パラメーターは、次の条件下で現在のインスタンスと等しいと見なされます。
私はこれを書いて成功させました。
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);
}
}
回避策として、現在、キーを単一の文字列に単純に連結しています。もちろん、これは非文字列キーでは機能しません。答えも知りたいです。
Wintellectの PowerCollections ( CodePlexダウンロード )をご覧ください。彼らのMultiDictionaryはそのようなことをしていると思います。
辞書の辞書なので、各オブジェクトにアクセスするための2つのキーがあります。メイン辞書のキーは必要なサブ辞書を取得し、次にサブ辞書の2番目のキーは必要なアイテムを取得します。それはあなたの言うことですか?
短くて必要な構文糖を提供するので、私はこれを頻繁に使用します...
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」キーも存在する必要はありません。
何か問題はありますか
新しい辞書<KeyValuePair <オブジェクト、オブジェクト>、オブジェクト>
私はこれをグーグルで検索しました: http://www.codeproject.com/KB/recipes/multikey-dictionary.aspx 。 structを使用して通常の辞書に2つのキーを含めることと比較した場合の主な機能は、2つのキーを指定する代わりに、後でキーの1つで参照できることです。
誰かが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;
}
}
Tuple2のようなクラスが必要だと思います。 GetHashCode()およびEquals()が含まれている2つの要素に基づいていることを確認してください。
C#のタプル を参照してください
Dictionary<TKey1,Dictionary<TKey2,TValue>>
を使用できますか?
これをサブクラス化することもできます:
public class DualKeyDictionary<TKey1,TKey2,TValue> : Dictionary<TKey1,Dictionary<TKey2,TValue>>
編集:これは重複した回答になりました。また、実用性にも限界があります。 「動作」し、dict[key1][key2]
をコーディングする機能を提供しますが、「正常に動作する」ようにするための「回避策」がたくさんあります。
HOWEVER:キックだけのために、それでも辞書を実装できますが、この時点で少し冗長になります:
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();
}
}
}
これが私の実装です。 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"];
辞書でTupleクラスを使用する別の例を次に示します。
// 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)];
}