Сохранить словарь <string, string> в настройках приложения - PullRequest
32 голосов
/ 28 мая 2009

У меня есть словарь строк, которые я хочу, чтобы пользователь мог добавить / удалить информацию, а затем сохранить ее для них, чтобы они могли получить к ней доступ при следующем перезапуске программы

Мне неясно, как сохранить словарь в качестве параметра. Я вижу, что в system.collections.special есть вещь, называемая stringdictionary, но я читал, что SD устарели и не должны использоваться.

также в будущем мне может понадобиться сохранить словарь, который содержит не только строки (int string)

как бы вы сохранили словарь в файле настроек для приложения .net?

Ответы [ 9 ]

43 голосов
/ 01 июня 2011

Вы можете использовать этот класс, производный от 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>
...
15 голосов
/ 28 мая 2009

Самый простой ответ - использовать разделитель строк и столбцов, чтобы преобразовать ваш словарь в одну строку. Тогда вам просто нужно сохранить 1 строку в файле настроек.

4 голосов
/ 10 октября 2012

Если вам не нужно использовать конструктор настроек или редактировать настройки с помощью текстового редактора, вы можете создать простой класс, производный от 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 не будет работать для словаря.

Используйте это в своем коде как обычные настройки:

// 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 (см. Использование пользовательских классов с настройками приложения ).

3 голосов
/ 28 мая 2009

Рассматривали ли вы использовать XML для хранения своего словаря? Это обеспечит определенную степень расширяемости, если в будущем вы решите, что хотите иметь возможность хранить другие типы словарей. Вы можете сделать что-то вроде:

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

Возможно, это излишне, но вы также будете готовы к тому, что захотите хранить более сложные данные, такие как пользовательские объекты.

3 голосов
/ 28 мая 2009

Кроме того, что предлагает Дэвид , я бы посмотрел в альтернативное хранилище для словаря. В конечном итоге объект «Параметры» сериализуется на диск.

2 голосов
/ 03 августа 2010

Вы также можете использовать 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);
}
1 голос
/ 22 октября 2010

Вы можете хранить StringCollection. похоже на это решение .

Я сделал 2 метода расширения для преобразования между StringCollection и Dictionary. Это самый простой способ, о котором я мог подумать.

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 голос
/ 22 октября 2010

Редактировать: Это вернет Hashtable (по любой причине, несмотря на то, что DictionarySectionHandler). Однако из-за того, что Hashtables и Dictionaries очень похожи, это не должно быть большой проблемой (хотя я понимаю, что словари новее, параметризованы и т. Д .; я бы предпочел сам диктатор, но это то, что дает нам .NET). *


Лучший ответ, который я только что нашел, это здесь . Он возвращает безопасную коллекцию без каких-либо путаниц в коде, чтобы преобразовать ее, и вы создаете очевидную (и простую) коллекцию в своем файле .config. Я использую это, и это довольно просто для любого будущего программиста (включая вас). Это обеспечивает более строгую типизацию и большую гибкость без чрезмерно сложного и ненужного анализа.

1 голос
/ 28 мая 2009

Вы можете создать пользовательский класс, который предоставляет словарь как открытое свойство. Затем вы можете указать этот пользовательский тип в качестве типа для вашей настройки.

Редактировать:

Я только что прочитал, что по какой-то причине универсальный словарь не может быть сериализован в XML, поэтому мое решение, вероятно, не будет работать (хотя я его не проверял ...). Это странно, потому что общий список можно сериализовать без проблем.

Вы все еще можете создать пользовательский класс, который можно установить как пользовательский параметр, но вам нужно будет отобразить список как свойство вместо словаря.

...