web-dev-qa-db-ja.com

アプリケーション設定にディクショナリ<string、string>を保存します

ユーザーが情報を追加/削除できるようにする文字列のディクショナリを持っているので、次にプログラムを再起動したときにアクセスできるように保存します。

辞書を設定として保存する方法がわかりません。 system.collections.specialの下にstringdictionaryと呼ばれるものがあることがわかりますが、SDは古くなっていて使用すべきではないということがよくわかります。

また、将来的には文字列だけではない(int文字列)辞書を保存する必要があるかもしれません

.netアプリケーションの設定ファイルに辞書をどのように保存しますか?

34
Crash893

最も簡単な答えは、行と列の区切り文字を使用して辞書を単一の文字列に変換することです。次に、設定ファイルに1つの文字列を格納するだけです。

17
David

StringDictionaryから派生したこのクラスを使用できます。アプリケーションの設定に役立つように、IXmlSerializableを実装しています。または、同様のアプローチを使用して、独自のXmlSerializableクラスを実装できます。

public class SerializableStringDictionary : StringDictionary, IXmlSerializable
{
    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        while (reader.Read() &&
            !(reader.NodeType == XmlNodeType.EndElement && reader.LocalName == this.GetType().Name))
        {
            var name = reader["Name"];
            if (name == null)
                throw new FormatException();

            var value = reader["Value"];
            this[name] = value;
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        foreach (DictionaryEntry entry in this)
        {
            writer.WriteStartElement("Pair");
            writer.WriteAttributeString("Name", (string)entry.Key);
            writer.WriteAttributeString("Value", (string)entry.Value);
            writer.WriteEndElement();
        }
    }
}

結果のXMLフラグメントは次のようになります。

...
<setting name="PluginSettings" serializeAs="Xml">
    <value>
        <SerializableStringDictionary>
            <Pair Name="property1" Value="True" />
            <Pair Name="property2" Value="05/01/2011 0:00:00" />
        </SerializableStringDictionary>
    </value>
</setting>
...
44

設定デザイナーを使用したり、テキストエディターで設定を編集したりする必要がない場合は、ApplicationSettingsBaseから派生する単純なクラスを作成できます。

namespace MyNamespace
{
    using System.Collections.Generic;
    using System.Configuration;

    /// <summary>
    /// Persistent store for my parameters.
    /// </summary>
    public class MySettings : ApplicationSettingsBase
    {
        /// <summary>
        /// The instance lock.
        /// </summary>
        private static readonly object InstanceLock = new object();

        /// <summary>
        /// The instance.
        /// </summary>
        private static MySettings instance;

        /// <summary>
        /// Prevents a default instance of the <see cref="MySettings"/> class 
        /// from being created.
        /// </summary>
        private MySettings()
        {
            // don't need to do anything
        }

        /// <summary>
        /// Gets the singleton.
        /// </summary>
        public static MySettings Instance
        {
            get
            {
                lock (InstanceLock)
                {
                    if (instance == null)
                    {
                        instance = new MySettings();
                    }
                }

                return instance;
            }
        }

        /// <summary>
        /// Gets or sets the parameters.
        /// </summary>
        [UserScopedSetting]
        [SettingsSerializeAs(SettingsSerializeAs.Binary)]
        public Dictionary<string, string> Parameters
        {
            get
            {
                return (Dictionary<string, string>)this["Parameters"];
            }

            set
            {
                this["Parameters"] = value;
            }
        }
    }
}

本当のトリックは、 [SettingsSerializeAs(SettingsSerializeAs.Binary)] 属性です。ほとんどの(すべての?)クラスはこの方法でシリアル化できますSettingsSerializeAs.StringまたはSettingsSerializeAs.Xmlは、Dictionaryでは機​​能しません。

通常の設定と同じように、コードでこれを使用します。

// this code untested...
MySettings.Instance.Parameters["foo"] = "bar";
MySettings.Instance.Parameters.Save();
MySettings.Instance.Parameters.Reload();
string bar;
if (!MySettings.Instance.Parameters.TryGetValue("foo", out bar))
{
    throw new Exception("Foobar");
}

ディクショナリをユーザーが編集可能な何かにシリアル化する場合は、ディクショナリから派生し、TypeConverterを操作する必要があります( アプリケーション設定でのカスタムクラスの使用 を参照)。

4
mheyman

辞書の保存にXMLを使用することを検討しましたか?将来、他の種類の辞書を保存できるようにしたい場合は、ある程度の拡張性が得られます。あなたは次のようなことをするかもしれません:

<dictionary>
   <entry key="myKey">
      [whatever data you like]
   </entry>
</dictionary>

やり過ぎかもしれませんが、カスタムオブジェクトなどのより複雑なデータを格納する場合にも備えておく必要があります。

3
Ender

Davidの提案 のようなことをする以外に、辞書の代替ストレージを調べます。最終的に、Settingsオブジェクトはディスクにシリアル化します。

3
Erik Forbes

System.Collections.Specialized.StringCollectionを使用して、キーを偶数のインデックスに配置し、値を奇数のインデックスに配置することもできます。

/// <summary>
/// Emulate a Dictionary (Serialization pb)
/// </summary>
private static string getValue(System.Collections.Specialized.StringCollection list, string key)
{
    for (int i = 0; i * 2 < list.Count; i++)
    {
        if (list[i] == key)
        {
            return list[i + 1];
        }
    }
    return null;
}

/// <summary>
/// Emulate a Dictionary (Serialization pb)
/// </summary>      
private static void setValue(System.Collections.Specialized.StringCollection list, string key, string value)
{
    for (int i = 0; i * 2 < list.Count; i++)
    {
        if (list[i] == key)
        {
            list[i + 1] = value;
            return;
        }
    }
    list.Add(key);
    list.Add(value);
}
2

StringCollectionを保存できます。それは このソリューションに に似ています。

StringCollectionDictionaryの間で変換する2つの拡張メソッドを作成しました。これは私が考えることができる最も簡単な方法です。

public static class Extender
{
    public static Dictionary<string, string> ToDictionary(this StringCollection sc)
    {
        if (sc.Count % 2 != 0) throw new InvalidDataException("Broken dictionary");

        var dic = new Dictionary<string, string>();
        for (var i = 0; i < sc.Count; i += 2)
        {
            dic.Add(sc[i], sc[i + 1]);
        }
        return dic;
    }

    public static StringCollection ToStringCollection(this Dictionary<string, string> dic)
    {
        var sc = new StringCollection();
        foreach (var d in dic)
        {
            sc.Add(d.Key);
            sc.Add(d.Value);
        }
        return sc;
    }
}

class Program
{
    static void Main(string[] args)
    {
        //var sc = new StringCollection();
        //sc.Add("Key01");
        //sc.Add("Val01");
        //sc.Add("Key02");
        //sc.Add("Val02");

        var sc = Settings.Default.SC;

        var dic = sc.ToDictionary();
        var sc2 = dic.ToStringCollection();

        Settings.Default.SC = sc2;
        Settings.Default.Save();
    }
}
1
BrunoLM

編集:これはHashtableを返します( 'DictionarySectionHandler'であるにもかかわらず、何らかの理由で)。ただし、ハッシュテーブルとディクショナリは非常に似ているので、大きな問題ではないはずです(ディクショナリの方が新しい、パラメータ化されているなどと気づきましたが、私は自分でディクショナリを好んでいましたが、これは.NETが提供するものです)。


これについて私が見つけた最良の答えは here です。これは、コードを混乱させずにタイプセーフなコレクションを返し、それを変換して、.configファイルに明白な(そして単純な)コレクションを作成します。私はこれを使用しており、将来のプログラマ(自分を含む)にとっては非常に簡単です。過度に複雑で不要な解析を行わずに、より強力なタイピングと柔軟性を実現します。

1
James

辞書をパブリックプロパティとして公開するカスタムクラスを作成できます。次に、このカスタムタイプを設定のタイプとして指定できます。

編集:

私はそれを読んだばかりですが、何らかの理由で汎用ディクショナリをXMLシリアル化できないため、私のソリューションはおそらく機能しません(ただし、テストしていません...)。ジェネリックリストは問題なくシリアル化できるため、これは奇妙です。

ユーザー設定として設定できるカスタムクラスを作成することもできますが、リストを辞書ではなくプロパティとして公開する必要があります。

1
Meta-Knight