2

.NETDictionary<TKey, TValue>オブジェクトでは、次のようにキー/値を割り当てることができます。

Dictionary<string, string> dict = new Dictionary<string, string>();
dict["1"] = "foo";
dict["2"] = "bar";

しかし、私はそのような辞書を使用することはできません:

Dictionary<string, string> dict = new Dictionary<string, string>();
dict["F1"]["F2"]["F3"] = "foo";
dict["2"]["X"] = "bar";

ネストできるコレクションが.NETにあります[]か、それとも自分で作成する必要がありますか?

自分で作成する必要がある場合、どうすればよいですか?

編集:

次のように、一意のキーを期待する実装があれば便利です。

dict["F1"]["F2"]["F3"] = "foo";
dict["F1"]["F2"]["F3"] = "bar"; //result is "bar" because "foo" was overridden

キーを複数回使用できる実装

dict["F1"]["F2"]["F3"] = "foo";
dict["F1"]["F2"]["F3"] = "bar"; //result can be "foo" and "bar"

これは可能ですか?

編集(ジョンスキートの質問による):

私はそのような構造を使用したいと思います(非常に大まかな例として):

json["data"]["request"]["name"] = "username";
json["data"]["request"]["pass"] = "password";

に解決します

{ data: { request: { name: "username", pass: "password" } } }

同様に、XMLなどに相当するものがあります。

4

6 に答える 6

15

任意に長いネストが必要なため、次の解決策を考え出しました。私のテストによれば、これは私が見る限り、壊れることはありません。

public class NestedDictionary<K, V> : Dictionary<K, NestedDictionary<K, V>>
    {
        public V Value { set; get; }

        public new NestedDictionary<K, V> this[K key]
        {
            set { base[key] = value; }

            get
            {
                if (!base.Keys.Contains<K>(key))
                {
                    base[key] = new NestedDictionary<K, V>();
                }
                return base[key];
            }
        }
    }

テスト:

NestedDictionary<string, string> dict = new NestedDictionary<string, string>();
dict["one"].Value = "Nest level 1";
dict["one"]["two"]["three"].Value = "Nest level 3";
dict["FieldA"]["FieldB"].Value = "Hello World";

Console.WriteLine(dict["one"].Value);
Console.WriteLine(dict["one"]["two"]["three"].Value);
Console.WriteLine(dict["FieldA"]["FieldB"].Value);
于 2012-09-22T12:02:03.773 に答える
12

これは、標準の辞書を使用して行うことができます。ネストを宣言するだけです。

Dictionary<string, Dictionary<string, string>> dict = ...
string test = dict["first"]["second"]

Dictionary<string, Dictionary<string, Dictionary<string, string>>> dict = ...
string test = dict["first"]["second"]["third"]

etc
于 2012-09-22T10:53:31.290 に答える
1

vb6で動作するように作成された元のCOMオブジェクトは、対応する名前Dictionaryのタイプの新しいアイテムを作成することにより、存在しないアイテムにアクセスする試みに応答します。このアプローチにより、最初にを作成しなくてDictionaryも、何かを保存できます。このアプローチの問題は、への書き込みを実行するときに追加したいが、たとえば評価しようとした場合、そのようなアイテムを作成したくないということです。MyDict["Foo"]["Bar"]MyDict["Foo"]"Foo"MyDictMyDict["Foo"]["Bar"]MyDict["Foo"]["Bar"].ValueOrDefault(someDefaultValue)

このようなコレクションを使用したのは、特定のものをモデル化するのに便利なためです(概念的には、XMLドキュメントによく似ています)。実行可能なアプローチの1つは、他の辞書しか含まない辞書は、意味的には非エンティティと見なされ、任意の機会に削除される可能性があることを宣言することです。サブコレクションを暗黙的に追加する場合は、追加するアイテムにフラグを設定して、削除される可能性のあるアイテムをチェックする必要があることを示します(または、そのようなアイテムがいくつ存在するかを示すカウンターを保持します)。次に、ある程度の頻度で、辞書をスキャンして、そのような「死んだ」アイテムを削除します。

別のアプローチは、ディクショナリからのインデクサーが実際のアイテムを返さないようにすることですが、代わりに「エフェメラルインデクサー」タイプを返します。これは、親オブジェクトへの参照を保持し、それにチェーンバックする内部メソッド、、、、を持ちますGetNestedForReading。次に、ステートメントは効果的に実行されますが、効果的に実行されます。存在しないキーを使用してメソッドを呼び出すと、新しいネストされたアイテムが作成されて返されます。メソッドは不変の「空の」アイテムになります。したがって、このアプローチを使用すると、空のアイテムの作成を回避できます。SetNestedForReadingGetValueSetValueFoo["Bar"]["Boz"] = "George";Foo.SetNestedForReading("Bar").SetValue("Boz", "George");z = Foo["Bar"]["Boz"];Foo.GetNestedForReading("Bar").GetValue("Boz");SetNestedForReadingGetNestedForReading

後者のアプローチは前者よりも複雑ですが、別の利点があります。各ノードに、共有された深く不変の辞書または共有されていない可変の辞書として、そのコレクションを個別に保持させることができます。もしGetNestedForWritingcallは、ネストされたオブジェクトが不変であることを確認し、同じアイテムを保持する新しい浅く変更可能なオブジェクトを構築できます。可変ノードのクローン作成方法を、すべてのサブノードの(不変の)クローンで新しい不変ノードを作成するものとして定義し、不変ノードのクローン作成方法をそれ自体を返すものとして定義すると、ほとんど不変であるツリーのクローン作成は非常に安価になります。各レベルに16個のアイテム(合計65,536個のリーフノード)を持つ新しくクローンされた(したがって不変の)4レベルツリーがあり、すべてのノードが共有不変である場合、リーフノードを更新するには1つのリーフと他の4つのノードを置き換えるだけで済みます。可変のもので。ツリーを再度複製するには、変更可能なノードに置き換えられたノードに対して新しい不変オブジェクトを作成するだけで済みます(たとえば、5つのものをコピーする)。完全に変更可能なツリーの便利さはありますが、

このアプローチで私が目にする最大の「問題」は、いくつかの奇妙な振る舞いを避けるために、のような構文の使用を要求しなければならないということですMyDict["Foo"]["Bar"].Value = "George"。その要件を回避するために暗黙の変換演算子が使用された場合、誰かがその時点で保持されているもののスナップショットとしてvar st = MyThing["Foo"]["Bar"];定義するようなステートメントを期待するでしょう。代わりに、インデックスを作成するものとして定義します。そのようなタイプから文字列を読み書きするために使用する必要がある場合、変数が文字列ではなかったという事実は明らかです。暗黙の演算子を使用してそのような割り当てを許可した場合、動作は奇妙になります。関数が「この戻り値を型推論に使用することを許可しない」と指定する方法がないのは残念です。ststringMyThing["Foo"]["Bar"]MyThing["Foo"]["Bar"].Value

ちなみに、インデクサー型をクラスまたはジェネリック構造体にすることは可能です。foo["Bar"]["boz"]["baz"]...クラスの場合、ネストされたNディープへのアクセスには、N一時的なヒープオブジェクトの作成が必要になる可能性があります。ジェネリック構造体の場合、構造体の作成が必要になりますNが、より深くネストされた構造体は大きくなります。適度なレベルのネストの場合、一般的な構造体の方がおそらくわずかに効率的ですが、クラスの操作はおそらく簡単です。

于 2012-09-22T17:28:34.623 に答える
0

Dictionaryとして使用TValue

var dict2 = new Dictionary<string, Dictionary<string, string>>();
var dict3 = new Dictionary<string, Dictionary<string, Dictionary<string, string>>>();

例えば:

var dict =
    new Dictionary<string, Dictionary<string, string>>
        {
            {
                "F1", new Dictionary<string, string>
                            {
                                {"F2", "foo"}
                            }
                }
        };


dict["F1"]["F2"] = "bar";
于 2012-09-22T10:54:44.653 に答える
0

検索する文字列キーの数を固定するか、キーの数が変わる可能性がある場合は、より一般的なキーメカニズムを提供するかを決定する必要があります。最初のケースでは、次のことを試してください。

Dictionary<string,Dictionary<string,string>> dict =
    Dictionary<string,Dictionary<string,string>>();
dict["F1"]["F2"] = "foo";
Dictionary<string,Dictionary<string,Dictionary<string,string>>> dict2 =
    Dictionary<string,Dictionary<string,string>>();
dict2["F1"]["F2"]["F3"] = "bar";

2番目のケースでは、次のように実行できます。

Dictionary<string[],string> dict = new Dictionary<string[],string>(new MyEqualityComparer());
dict[new string[] {"F1","F2"}] = "foo";
dict[new string[] {"F1","F2","F3"}] = "bar";

MyEqualityComparerクラスは次のようになります。

public class MyEqualityComparer : IEqualityComparer<string[]>
{
    public int GetHashCode(string[]item)
    {
         int hashcode = 0;
         foreach (string s in item)
         { 
             hashcode |= s.GetHashCode();
         }
         return hashcode;
    }

    public bool Equals(string [] a, string [] b)
    {
         if (a.Length != b.Length)
             return false;
         for (int i = 0; i < a.Length; ++i)
         {
             if (a[i] != b[i])
                 return false;
         }
         return true;
   }
于 2012-09-22T11:00:31.217 に答える
0

あなたのケースは使うのに良い場所だと思いますDynamicObjectDictionary<string, object>内部で使用するjsonの例を作成します。

同じアイデアをxmlにも使用できます。

string json = @"{""Name"":""Joe"",
                 ""Age"":30,
                 ""Address"":{ ""City"":""NY"" }}";

dynamic dynObj = new DynamicJson(json);

Console.WriteLine(dynObj.Name);
Console.WriteLine(dynObj.Age);
Console.WriteLine(dynObj.Address.City);

-

public class DynamicJson : DynamicObject
{
    Dictionary<string, object> _Dict;

    public DynamicJson(string json)
    {
        _Dict = (Dictionary<string, object>)new JavaScriptSerializer().DeserializeObject(json); 
    }

    DynamicJson(Dictionary<string, object> dict)
    {
        _Dict = dict;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = null;
        object obj;
        if (!_Dict.TryGetValue(binder.Name, out obj)) return false;

        if (obj is Dictionary<string, object>)
        {
            result = new DynamicJson((Dictionary<string, object>)obj);
        }else
        {
            result = obj;
        }
        return true;
    }
}
于 2012-09-22T11:48:50.830 に答える