【问题标题】:How to store a HashTable in the usersettings?如何在用户设置中存储哈希表?
【发布时间】:2009-11-21 11:34:03
【问题描述】:

在 .NET 中,您可以选择哈希表作为用户设置的类型。但是,当我以这种方式保存并检索它时,它似乎根本没有保存它。

Hashtable t = new Hashtable();
t.Add(1,"Foo");
t.Add(2,"Bar");
Properties.Settings.Default.Setting = t;
Properties.Settings.Default.Save();

if(Properties.Settings.Default.Setting != null)
        foreach (DictionaryEntry entry in Properties.Settings.Default.Setting)
        {
            MessageBox.Show(entry.Key + " " + entry.Value);
        }

当我可以在 Visual Studio 中清楚地选择该类型时,为什么它不在用户设置中序列化它?我会理解如果这是未列出的类型(例如字典)的情况,但列出了 Hashtable。我该如何解决这个问题?
按此顺序排列的简单性和效率对我来说是最高优先级。

非常感谢, 卡夫


更新:

@Joao ,非常感谢二进制解决方案。我觉得它很有趣,很干净。将其序列化为二进制文件的一个缺点可能是您无法再手动更改用户设置文件中的任何内容。但我认为无论如何很少会这样做,所以这是一个很好的解决方案。

我正在考虑一种不同的方法来在用户范围内创建字符串类型的“XMLSetting”字段,并使用此代码将值存储和检索为序列化为哈希表的 XMl 文件。但我确信这不是最好的方法,除了我在下面做的事情之外,有没有人知道在用户设置中将哈希表/字典序列化为 xml 的更好方法?

if(string.IsNullOrEmpty(Properties.Settings.Default.XMLSetting))
            {
                Console.WriteLine("Usersettings is empty. Initializing XML file...");
                XmlDocument doc = new XmlDocument();
                XmlElement hashtable = doc.CreateElement("HashTable");
                doc.AppendChild(hashtable);

                GenerateValues(doc, hashtable, "1", "Foo");
                GenerateValues(doc, hashtable, "2", "Bar");

                Properties.Settings.Default.XMLSetting = doc.OuterXml;
                Properties.Settings.Default.Save();
            }
            else
            {
                Console.WriteLine("Retrieving existing user settings...");
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(Properties.Settings.Default.XMLSetting);

                Hashtable hashtable = new Hashtable();

                foreach (XmlNode entry in doc.DocumentElement.ChildNodes)
                {
                    hashtable.Add(int.Parse(entry.FirstChild.InnerText), entry.FirstChild.NextSibling.InnerText);
                }

                foreach (DictionaryEntry entry in hashtable)
                {
                    Console.WriteLine(entry.Key + " " + entry.Value);
                }
            }

private static void GenerateValues(XmlDocument doc, XmlElement hashtable, string skey, string svalue)
        {
            XmlElement entry = doc.CreateElement("entry");
            XmlElement key = doc.CreateElement("Key");
            XmlElement value = doc.CreateElement("Value");
            entry.AppendChild(key);
            entry.AppendChild(value);

            key.AppendChild(doc.CreateTextNode(skey));
            value.AppendChild(doc.CreateTextNode(svalue));

            hashtable.AppendChild(entry);
        }

【问题讨论】:

  • 我刚刚测试了将 Hashtable 持久化为用户设置,并没有发现任何问题。如果您还没有这样做,请尝试在新的测试项目中从头开始。
  • 嗨,我刚刚又试了一次。它一直在说 Properties.Settings.Default.Setting 保持为空,即使在我保存并重新启动应用程序之后也是如此。不过,它适用于其他类型的字符串。你是怎么让它工作的?你能以某种方式发布你的解决方案吗?
  • 我的测试太仓促了。 Hashtable 不会在使用默认设置类的应用程序运行之间持久化。但是,如果拥有一个 Hashtable 对您来说至关重要并且不介意一些手动工作,您可以查看我的答案。

标签: .net hashtable settings


【解决方案1】:

Hashtable 不支持序列化为 XML,我也不相信是简单的字符串。当您使用 Settings.settings 文件和关联的自动生成的类时,这是两个可用的序列化选项。

但是,如果您自己创建设置类并管理 App.Config 部分,则可以使用二进制序列化来持久化 Hastable。

请参阅以下示例。这是一个包含以下文件的控制台应用程序:

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <sectionGroup 
      name="userSettings" 
      type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
      <section 
        name="ConsoleApplication1.MyCustomSettings" 
        type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 
        allowExeDefinition="MachineToLocalUser" 
        requirePermission="false" />
    </sectionGroup>
  </configSections>
  <userSettings>
    <ConsoleApplication1.MyCustomSettings>
      <setting name="MyHashtable" serializeAs="Binary">
        <value></value>
      </setting>
      <setting name="MyBackColor" serializeAs="String">
        <value>Silver</value>
      </setting>
    </ConsoleApplication1.MyCustomSettings>
  </userSettings>
</configuration>

手动创建的自定义设置类:

public class MyCustomSettings : ApplicationSettingsBase
{
    private static MyCustomSettings defaultInstance = (
        (MyCustomSettings)
        (ApplicationSettingsBase.Synchronized(new MyCustomSettings())));

    public static MyCustomSettings Default
    {
        get { return defaultInstance; }
    }

    [UserScopedSettingAttribute()]
    [DebuggerNonUserCodeAttribute()]
    [DefaultSettingValueAttribute("Silver")]
    public Color MyBackColor
    {
        get { return ((Color)(this["MyBackColor"])); }
        set { this["MyBackColor"] = value; }
    }

    [UserScopedSettingAttribute()]
    [DebuggerNonUserCodeAttribute()]
    [SettingsSerializeAs(SettingsSerializeAs.Binary)]
    public Hashtable MyHashtable
    {
        get { return ((Hashtable)(this["MyHashtable"])); }
        set { this["MyHashtable"] = value; }
    }
}

程序.cs

class Program
{
    static void Main(string[] args)
    {
        // For the first time no Hastable will exist.
        // Create one with the default values
        if (MyCustomSettings.Default.MyHashtable == null)
        {
            Console.WriteLine("Initializing Hashtable...");

            MyCustomSettings.Default.MyHashtable = new Hashtable();

            MyCustomSettings.Default.MyHashtable.Add(1, "foo");
            MyCustomSettings.Default.MyHashtable.Add(2, "bar");

            MyCustomSettings.Default.Save();
        }

        foreach (DictionaryEntry entry in MyCustomSettings.Default.MyHashtable)
        {
            Console.WriteLine(entry.Key + ": " + entry.Value);
        }

        Console.ReadKey();
    }
}

更新: 如果您想要数据的人类可读表示,那么您使用的方法似乎是合理的。尽管如此,您也可以尝试另一种方法,更好地封装转换为字符串 (XML) 和从字符串 (XML) 转换的逻辑。

这种方法允许您使用 IDE 对 Settings.settings 文件的支持,从而无需生成自定义设置类或弄乱 App.config。

您只需要实现一个自定义类来保存您的数据,在我的示例中,我将从 StringDictionary 继承这个类,并实现一个TypeConverter,设置系统将使用它来以字符串格式保存数据。

[TypeConverter(typeof(StringDictionaryTypeConverter))]
public class MyStringDictionary : StringDictionary
{
}

public class StringDictionaryTypeConverter : TypeConverter
{
    public override bool CanConvertFrom(
        ITypeDescriptorContext context, 
        Type sourceType)
    {
        if (sourceType.Equals(typeof(string)))
        {
            return true;
        }

        return base.CanConvertFrom(context, sourceType);
    }

    public override bool CanConvertTo(
        ITypeDescriptorContext context, 
        Type destinationType)
    {
        if (destinationType.Equals(typeof(string)))
        {
            return true;
        }

        return base.CanConvertTo(context, destinationType);
    }

    public override object ConvertFrom(
        ITypeDescriptorContext context, 
        CultureInfo culture, 
        object value)
    {
        if (value is string)
        {
            MyStringDictionary sd = new MyStringDictionary();

            XDocument xs = XDocument.Load(new StringReader(value as string));

            foreach (var item in xs.Descendants("entry"))
            {
                sd.Add(item.Element("key").Value, item.Element("value").Value);
            }

            return sd;
        }

        return base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(
        ITypeDescriptorContext context, 
        CultureInfo culture, 
        object value, 
        Type destinationType)
    {
        if (destinationType.Equals(typeof(string)))
        {
            MyStringDictionary sd = value as MyStringDictionary;

            StringBuilder sb = new StringBuilder();

            sb.Append("<entries>");
            foreach (DictionaryEntry item in sd)
            {
                sb.AppendFormat(
                    "<entry><key>{0}</key><value>{1}</value></entry>", 
                    item.Key, 
                    item.Value);
            }
            sb.Append("</entries>");

            return sb.ToString();
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }
}

现在您只需使用 MyStringDictionary 类作为设置的数据类型。由于 Visual Studio 未在用户设置的可用数据类型中显示用户类,您需要执行一次性解决方法,包括使用 XML 编辑器(右键单击并打开宽度)打开 Settings.settings 文件并手动指定用户设置的类型为 MyStringDictionary 的全名。

希望这会有所帮助。

【讨论】:

  • 这工作现在非常感谢。我还为这个问题添加了一个新的解决方案。你怎么看?有什么优化建议吗?
  • 非常感谢。这也很有效。现在我们对这个问题有了三个解决方案。 :o)
  • 这里有一个更好的实现,它使用“真正的”XML 而不是手动输入 等内容:bytes.com/topic/c-sharp/answers/…
  • 最后一点,要将新类型作为您设置的数据类型,首先构建应用程序,然后在 Settings.settings 的类型列中单击浏览...屏幕,您会看到“选择类型”窗口。尝试在“选定类型”字段中输入完全限定的类型名称,然后单击“确定”。它对我有用。
猜你喜欢
  • 2012-11-12
  • 2013-08-27
  • 2017-07-31
  • 1970-01-01
  • 2017-01-22
  • 2015-07-18
  • 2015-05-26
  • 2011-11-17
  • 1970-01-01
相关资源
最近更新 更多