将 Dictionary<string,string> 存储在应用程序设置中

Posted

技术标签:

【中文标题】将 Dictionary<string,string> 存储在应用程序设置中【英文标题】:Store Dictionary<string,string> in application settings 【发布时间】:2010-10-29 15:40:48 【问题描述】:

我有一个字符串字典,我希望用户能够从中添加/删除信息,然后为他们存储它,以便他们可以在下次程序重新启动时访问它

我不清楚如何将字典存储为设置。我看到在 system.collections.special 下有一个叫做 stringdictionary 的东西,但我读到 SD 已经过时并且不应该使用。

将来我可能还需要存储一个不仅仅是字符串的字典(int string)

如何在 .net 应用程序的设置文件中存储字典?

【问题讨论】:

相关:***.com/questions/2890271/… 【参考方案1】:

您可以使用从 StringDictionary 派生的此类。为了对应用程序设置有用,它实现了 IXmlSerializable。 或者您可以使用类似的方法来实现您自己的 XmlSerializable 类。

public class SerializableStringDictionary : System.Collections.Specialized.StringDictionary, System.Xml.Serialization.IXmlSerializable

    public System.Xml.Schema.XmlSchema GetSchema()
    
        return null;
    

    public void ReadXml(System.Xml.XmlReader reader)
    
        while (reader.Read() &&
            !(reader.NodeType == System.Xml.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(System.Xml.XmlWriter writer)
    
        foreach (System.Collections.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>
...

【讨论】:

我不知道为什么这个答案不被接受。很有用,谢谢! 为我工作,但我在 ReadXml 方法的顶部添加了this.Clear(); 语句,只是为了确保字典中没有任何过时的项目。 如何将其扩展到通用字典? 有设计师支持这个吗?【参考方案2】:

最简单的答案是使用行和列分隔符将您的字典转换为单个字符串。然后你只需要在设置文件中存储1个字符串。

【讨论】:

好主意 - 使用 JSON 序列化之类的东西来使这个过程相对轻松。 您能详细说明一下吗? JSON 序列化的形式是 "object": "key": "value", "key": "value" 在野外有 JSON 序列化库 - 您不必从头开始这样做;但如果你愿意,写一个非常简单的应该不会太难。 类似但更简单的是使用 StringCollection(可以作为设置存储)。然后,您只需要将键与值分开(不要使用 '\0' - .Net 设置代码很乐意使用零字节保存 StringCollection 设置条目,但以后不会加载它)。【参考方案3】:

如果您不需要使用设置设计器或使用文本编辑器编辑您的设置,您可以创建一个派生自 ApplicationSettingsBase 的简单类:: p>

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.StringSettingsSerializeAs.Xml 不适用于字典。

在您的代码中使用它,就像正常设置一样:

// 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");

如果您希望 Dictionary 序列化为用户可编辑的内容,您必须从 Dictionary 派生并使用 TypeConverter(参见 Using Custom Classes with Application Settings)。

【讨论】:

我在 MySettings.Instance.Parameters 上有一个 NullReferenceException ...知道吗?我不得不承认,我不知道那里发生了什么。【参考方案4】:

除了做David's suggests 之类的事情之外,我还会研究字典的备用存储。最终,Settings 对象序列化到磁盘。

【讨论】:

【参考方案5】:

您是否考虑过使用 XML 来存储您的字典?如果将来您决定希望能够存储其他类型的字典,这将提供一定程度的可扩展性。你可能会这样做:

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

可能有点矫枉过正,但如果您想要存储更复杂的数据(例如自定义对象),您也应该做好准备。

【讨论】:

【参考方案6】:

您还可以使用 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);

【讨论】:

【参考方案7】:

您可以创建一个自定义类,将 Dictionary 公开为公共属性。然后,您可以将此自定义类型指定为您的设置类型。

编辑:

我刚刚读到,由于某种原因,通用字典无法进行 XML 序列化,因此我的解决方案可能无法正常工作(虽然我还没有测试过......)。这很奇怪,因为通用列表可以毫无问题地被序列化。

您仍然可以创建一个可以设置为用户设置的自定义类,但您需要将列表公开为属性而不是字典。

【讨论】:

我不确定我是否遵循。创建一个包含字典的类,然后保存该类? 没错,但因为通用字典不可序列化(见编辑),它需要是一个列表。【参考方案8】:

编辑:这将返回一个哈希表(无论出于何种原因,尽管它是一个“DictionarySectionHandler”)。然而,由于 Hashtables 和字典非常相似,这应该不是一个大问题(尽管我意识到字典是更新的、参数化的等;我自己更喜欢字典,但这是 .NET 给我们的)。


我刚刚找到的最佳答案是here。它返回一个类型安全的集合,没有任何混乱的代码来转换它,并且您在 .config 文件中创建一个明显(且简单)的集合。我正在使用它,它对于任何未来的程序员(包括你自己)来说都是非常简单的。它允许更强的打字和更大的灵活性,没有任何过于复杂和不必要的解析。

【讨论】:

【参考方案9】:

您可以存储StringCollection。类似to this solution。

我做了 2 种扩展方法来在 StringCollectionDictionary 之间进行转换。这是我能想到的最简单的方法。

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();
    

【讨论】:

以上是关于将 Dictionary<string,string> 存储在应用程序设置中的主要内容,如果未能解决你的问题,请参考以下文章

泛型之Dictionary

ConcurrentDictionary和Dictionary对比

连接两个字典[重复]

将 [Dictionary<String,Any?>] 转换为 [Dictionary<String,Any!>] ?迅速 3

如何将 List<Dictionary<string, byte[]> 对象添加到 Dictionary<string, byte[]> [重复]

如何在 C# 中使用 LINQ 过滤嵌套字典?