Ответ 1
Самый простой ответ - использовать разделитель строк и столбцов для преобразования словаря в одну строку. Тогда вам просто нужно сохранить 1 строку в файле настроек.
У меня есть словарь строк, который я хочу, чтобы пользователь мог добавлять/удалять информацию, а затем хранить его для них, чтобы они могли получить к нему доступ в следующий раз, когда программа перезагрузится
Непонятно, как я могу хранить словарь в качестве параметра. Я вижу, что в system.collections.special есть вещь, называемая stringdictionary, но ive читает, что SD устарели и не должны использоваться.
Также в будущем мне может понадобиться хранить словарь, который не является только строками (int string)
как вы будете хранить словарь в файле настроек для приложения .net?
Самый простой ответ - использовать разделитель строк и столбцов для преобразования словаря в одну строку. Тогда вам просто нужно сохранить 1 строку в файле настроек.
Вы можете использовать этот класс, полученный из 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>
...
Вместо того, чтобы делать что-то вроде Дэвид предлагает, я бы посмотрел на альтернативное хранилище для словаря. В конечном итоге объект "Настройки" сериализуется на диск.
Рассматривали ли вы использование XML для хранения своего словаря? Это обеспечит определенную степень расширяемости, если в будущем вы решите, что хотите иметь возможность хранить другие типы словарей. Вы можете сделать что-то вроде:
<dictionary>
<entry key="myKey">
[whatever data you like]
</entry>
</dictionary>
Может быть, излишний, но вы также будете готовы в том случае, если хотите хранить более сложные данные, такие как пользовательские объекты.
Если вам не нужно использовать конструктор настроек или редактировать свои настройки с помощью текстового редактора, вы можете создать простой класс, который происходит из 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 (см. Использование пользовательских классов с настройками приложения).
Вы можете создать пользовательский класс, который предоставляет словарь как общедоступное свойство. Затем вы можете указать этот настраиваемый тип как тип для своих настроек.
Изменить:
Я только что прочитал, что по какой-то причине общий словарь не может быть сериализован XML, поэтому мое решение, вероятно, не сработает (я его еще не протестировал, хотя...). Это странно, потому что общий список можно сериализовать без каких-либо проблем.
Вы все равно можете создать пользовательский класс, который можно установить как пользовательский параметр, но вам нужно будет иметь список, открытый как свойство, а не словарь.
Вы также можете использовать 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);
}
Изменить: это вернет Hashtable (по какой-либо причине, несмотря на то, что это "DictionarySectionHandler" ). Однако, поскольку Hashtables и Dictionaries настолько схожи, это не должно быть большой проблемой (хотя я понимаю, что словари новее, параметризированы и т.д., Я бы предпочел сам диктонарий, но это то, что дает нам .NET).
Лучший ответ, который я только что нашел для этого, здесь. Он возвращает коллекцию типов с любым путаницей в коде, чтобы преобразовать ее, и вы создаете очевидную (и простую) коллекцию в вашем файле .config. Я использую это, и это довольно прямо для любого будущего программиста (включая самого себя). Это позволяет более набирать текст и больше гибкости, без чрезмерно сложного и ненужного разбора.
Вы можете сохранить StringCollection
. Это похоже на это решение.
Я сделал 2 метода расширения для преобразования между StringCollection
и a 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();
}
}