Как сохранить HashTable в настройках пользователя?
В.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);
}
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; }
}
}
Program.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 не отображает пользовательские классы в доступных типах данных для пользовательского параметра, вам нужно сделать одноразовый обходной путь, который заключается в открытии файла Settings.settings с помощью редактора XML (щелчок правой кнопкой мыши и открытие ширины) и указание вручную тип пользовательского параметра в качестве полного имени MyStringDictionary.
Надеюсь это поможет.