Как сохранить массив int [] в настройках приложения


93

Я создаю простое приложение Windows Forms с помощью C # express 2008. Я опытный разработчик на C ++, но в значительной степени новичок в C # и .NET.

В настоящее время я сохраняю некоторые из моих простых настроек приложения с помощью конструктора настроек и такого кода:

// Store setting  
Properties.Settings.Default.TargetLocation = txtLocation.Text;  
...  
// Restore setting  
txtLocation.Text = Properties.Settings.Default.TargetLocation;  

Теперь я хотел бы сохранить либо массив ints ( int[]), либо, возможно, List of ints ( List< int >) в качестве настройки. Однако я не могу понять, как это сделать. Я искал документацию, stackoverflow и google, и я не могу найти достойного объяснения, как это сделать.

Моя догадка, основанная на редких примерах, которые я нашел, заключается в том, что мне нужно создать сериализуемый класс, который обертывает мой массив или список, а затем я смогу использовать этот тип в конструкторе настроек. Однако я не совсем уверен, как это сделать.

Ответы:


137

Есть и другое решение - требуется немного ручного редактирования файла настроек, но после этого отлично работает в среде VS и в коде. И не требует дополнительных функций или оберток.

Дело в том, что VS позволяет сериализовать int[]тип по умолчанию в файле настроек - просто не позволяет выбрать его по умолчанию. Итак, создайте настройку с желаемым именем (например, SomeTestSetting) и сделайте ее любого типа (например, stringпо умолчанию). Сохраните изменения.

Теперь перейдите в папку своего проекта и откройте файл «Properties \ Settings.settings» в текстовом редакторе (например, в Блокноте). Или вы можете открыть его в VS, щелкнув правой кнопкой мыши в обозревателе решений на «-> Свойства -> Settings.settings». ", выберите" Открыть с помощью ... ", а затем выберите" Редактор XML "или" Редактор исходного кода (текстовый) ". В открывшихся настройках xml найдите свою настройку (она будет выглядеть так):

<Setting Name="SomeTestSetting" Type="System.String" Scope="User">
  <Value Profile="(Default)" />
</Setting>

Измените параметр "Тип" с System.Stringна System.Int32[]. Теперь этот раздел будет выглядеть так:

<Setting Name="SomeTestSetting" Type="System.Int32[]" Scope="User">
  <Value Profile="(Default)" />
</Setting>

Теперь сохраните изменения и снова откройте настройки проекта - вуаля! - У нас есть настройка SomeTestSetting с типом, к System.Int32[]которой можно получить доступ и отредактировать через VS Settings Designer (значения тоже), а также в коде.


5
Потрясающие. Чтобы ввести что-то в настройку с помощью редактора в Visual Studio, вы должны вставить что-то вроде этого, это для массива строк, что мне и понадобилось <? Xml version = "1.0" encoding = "utf-16"?> <ArrayOfString xmlns: xsi = " w3.org/2001/XMLSchema-instance " xmlns: xsd = " w3.org/2001/XMLSchema "> <string> String1 </string> <string> String2 </string> </ArrayOfString>
Karsten

9
+1 ... не понимаю, почему этот ответ не был принят ... дополнительный код не требуется (просто измените одну строку существующего xml), и вы получите безопасность типов и полную поддержку дизайнера!
Крис

1
Крис, спасибо :) Дело в том, что я добавил свой ответ намного позже, чем первоначально принятый ответ (примерно год спустя, на самом деле :)). Просто делюсь опытом :)
Jen-Ari

6
Для любого любопытного, синтаксис файла конфигурации XML для int[]будет выглядеть следующим образом ( за исключением довольно печати):<setting name="SomeTestSetting" serializeAs="String"><value><ArrayOfInt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><int>1</int><int>2</int><int>3</int></ArrayOfInt></value></setting>
aaaantoine

1
Согласен, это лучший ответ, поэтому я изменил оценку.
sidewinderguy

41

хранить:

string value = String.Join(",", intArray.Select(i => i.ToString()).ToArray());

воссоздать:

int[] arr = value.Split(',').Select(s => Int32.Parse(s)).ToArray();

Изменить: предложение Авеля!


Итак, похоже, вы вручную сериализуете данные в строку, и наоборот. Думаю, это сработает, я мог бы просто сохранить данные как строковую настройку. Я думал о том, чтобы сделать что-то подобное, но я искал что-то более «чистое», «стандартное» или просто более .NET. Я буду иметь это в виду, если ничего лучше не появится. Спасибо.
sidewinderguy

Привет, тогда я бы пошел с авеню Маккея! (раздел конфигурации / группа разделов)
Майк Глисон младший Кутюрье

Я собираюсь пойти с этим, потому что это просто. Всем спасибо за идеи, я уверен, что они помогут в будущем.
sidewinderguy

2
Примечание. System.LinqЧтобы описанный выше трюк сработал, вам нужно будет добавить к своим usings / import.
Шон Хэнли,

11

Есть еще один способ достичь этого результата, который намного чище в использовании, но требует большего количества кода. Я использую собственный преобразователь типов и типов, возможен следующий код:

List<int> array = Settings.Default.Testing;
array.Add(new Random().Next(10000));
Settings.Default.Testing = array;
Settings.Default.Save();

Для этого вам понадобится тип с преобразователем типов, который позволяет преобразовывать строки в строки и обратно. Это делается путем украшения типа атрибутом TypeConverterAttribute:

[TypeConverter(typeof(MyNumberArrayConverter))]
public class MyNumberArray ...

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

class MyNumberArrayConverter : TypeConverter
{
    public override bool CanConvertTo(ITypeDescriptorContext ctx, Type type)
    { return (type == typeof(string)); }

    public override bool CanConvertFrom(ITypeDescriptorContext ctx, Type type)
    { return (type == typeof(string)); }

    public override object ConvertTo(ITypeDescriptorContext ctx, CultureInfo ci, object value, Type type)
    {
        MyNumberArray arr = value as MyNumberArray;
        StringBuilder sb = new StringBuilder();
        foreach (int i in arr)
            sb.Append(i).Append(',');
        return sb.ToString(0, Math.Max(0, sb.Length - 1));
    }

    public override object ConvertFrom(ITypeDescriptorContext ctx, CultureInfo ci, object data)
    {
        List<int> arr = new List<int>();
        if (data != null)
        {
            foreach (string txt in data.ToString().Split(','))
                arr.Add(int.Parse(txt));
        }
        return new MyNumberArray(arr);
    }
}

Предоставляя некоторые удобные методы для класса MyNumberArray, которые мы затем можем безопасно назначать списку и из него, весь класс будет выглядеть примерно так:

[TypeConverter(typeof(MyNumberArrayConverter))]
public class MyNumberArray : IEnumerable<int>
{
    List<int> _values;

    public MyNumberArray() { _values = new List<int>(); }
    public MyNumberArray(IEnumerable<int> values) { _values = new List<int>(values); }

    public static implicit operator List<int>(MyNumberArray arr)
    { return new List<int>(arr._values); }
    public static implicit operator MyNumberArray(List<int> values)
    { return new MyNumberArray(values); }

    public IEnumerator<int> GetEnumerator()
    { return _values.GetEnumerator(); }
    IEnumerator IEnumerable.GetEnumerator()
    { return ((IEnumerable)_values).GetEnumerator(); }
}

Наконец, чтобы использовать это в настройках, вы добавляете указанные выше классы в сборку и компилируете. В редакторе Settings.settings вы просто нажимаете опцию «Обзор», выбираете класс MyNumberArray и начинаете.

Опять же, это намного больше кода; однако его можно применить к гораздо более сложным типам данных, чем простой массив.


Спасибо, это выглядит интересно. Я попробую, когда у меня будет возможность.
sidewinderguy

3

Укажите параметр как System.Collections.ArrayList, а затем:

Settings.Default.IntArray = new ArrayList(new int[] { 1, 2 });

int[] array = (int[])Settings.Default.IntArray.ToArray(typeof(int));

2

Простое решение - установить значение по умолчанию для параметра равным null в свойстве, но в конструкторе проверьте, имеет ли свойство значение null, и если да, то установите его фактическое значение по умолчанию. Итак, если вам нужен массив int:

public class ApplicationSettings : ApplicationSettingsBase
{
    public ApplicationSettings()
    {
        if( this.SomeIntArray == null )
            this.SomeIntArray = new int[] {1,2,3,4,5,6};
    }

    [UserScopedSetting()]
    [DefaultSettingValue("")]
    public int[] SomeIntArray
    {
        get
        {
            return (int[])this["SomeIntArray"];
        }
        set
        {
            this["SomeIntArray"] = (int[])value;
        }
    }
}

Это кажется хакерским, но чистым и работает должным образом, поскольку свойства инициализируются до их последних (или по умолчанию) настроек перед вызовом конструктора.


2
Дизайнер для файлов настроек приложения автоматически генерирует код, и поэтому подобное изменение будет перезаписано каждый раз, когда кто-либо использует конструктор, даже случайно.
Carl G

1

Б / у System.Object.

Пример:

byte[] arBytes = new byte[] { 10, 20, 30 };
Properties.Settings.Default.KeyObject = arBytes;

Извлечь:

arBytes = (byte[])Properties.Settings.Default.KeyObject;

1
Я попытался использовать System.Object, но настройки не сохранялись между сеансами. (Да, это была релизная сборка, автономная установка, как бы вы это ни называли, я не отлаживал с помощью IDE)
Эмануэль Винтила

0

Я думаю, что вы правы в сериализации своих настроек. См. Мой ответ на этот вопрос в качестве образца:

Способы обмена конфигурацией между двумя приложениями?

У вас будет свойство, которое представляет собой массив, например:

/// <summary>
/// Gets or sets the height.
/// </summary>
/// <value>The height.</value>
[XmlAttribute]
public int [] Numbers { get; set; }

0

Сделайте несколько функций, которые преобразуют массив int в строку, но между каждой поместите символ вроде "" (пробел).

Итак, если массив равен {1,34,546,56}, строка будет «1 34 645 56»

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.