如何在应用设置中存储int[]数组

102

我正在使用C# Express 2008创建一个简单的Windows Forms应用程序。我是一名有经验的C++开发人员,但在C#和.NET方面基本上是新手。

目前,我正在使用设置设计器和以下代码来存储我的某些简单应用程序设置:

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

我想将一个整数数组(int[])或整数列表(List< int >)存储为设置。但是,我不知道如何做到这一点。我在文档、stackoverflow和谷歌上搜索了很久,但都找不到一个好的解释如何实现。

根据我找到的零星示例,我的直觉是需要创建一个可序列化的类来包装我的数组或列表,然后我就可以在设置设计器中使用该类型。但是,我不确定具体应该如何实现。

8个回答

149

还有另一种解决方案 - 需要对设置文件进行手动编辑,但之后可以在VS环境和代码中正常工作,且不需要使用其他附加函数或封装。

问题在于,VS默认允许序列化int[]类型到设置文件中,但是默认情况下无法进行选择。

因此,创建一个所需名称的设置(例如SomeTestSetting),并将其设置为任何类型(例如默认的string)。 保存更改。

现在打开项目文件夹并使用文本编辑器(例如记事本)打开“Properties\Settings.settings”文件,或者您可以在Solution Explorer中右键单击“-> Properties -> Settings.settings”,选择“打开方式...”,然后选择“XML编辑器”或“源代码(文本)编辑器”来在VS中打开它。 在打开的xml设置文件中找到您的设置(它看起来像这样):

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

将“Type”参数从System.String更改为System.Int32[]。现在,这个部分将如下所示:

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

现在保存更改并重新打开项目设置 - 喔啦! - 我们有了类型为System.Int32[]的SomeTestSetting设置,可以通过VS设置设计器(包括值)以及代码进行访问和编辑。


5
了不起。如果你想在Visual Studio的编辑器中输入一些内容到设置中,你应该粘贴类似于这样的内容。下面是一个字符串数组的示例,这正是我所需的: <?xml version="1.0" encoding="utf-16"?> <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <string>String1</string> <string>String2</string> </ArrayOfString> - Karsten
9
+1..不理解为什么这个答案没有被接受...不需要额外的代码(只需修改现有XML文件的一行),就可以获得类型安全和完整的VS设计支持! - Chris
6
如果有人好奇,int[] 的配置文件 XML 语法看起来会像这样(未进行格式化):<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
2
@Prabu,在手动编辑设置文件后,您可以返回项目属性中的“设置”选项卡并在那里编辑值(只需单击值字段,您将在其末尾看到省略号按钮(...)。单击它,然后您就能指定您的值)。 - Jen-Ari
显示剩余5条评论

45

存储:

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

重新创建:

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

编辑:Abel 的建议!


好的,看起来你正在手动将数据序列化为字符串,反之亦然。我猜这会起作用,我可以将数据存储为字符串设置。我考虑过做类似的事情,但我正在寻找更“干净”、“标准”或者更符合.NET的东西。如果没有更好的选择出现,我会记住这个方法的。谢谢。 - sidewinderguy
嗨,我会选择麦凯的方法!(配置部分/部分组) - Mike Gleason jr Couturier
我会选择这个,因为它很简单。谢谢大家提供的所有想法,我相信它们在未来会有所帮助。 - sidewinderguy
2
请注意:为使上述技巧起作用,您需要将 System.Linq 添加到您的 using/imports 中。 - Sean Hanley

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类上提供一些便捷方法,我们可以安全地对List进行赋值和获取,完整的类大致如下:

[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

4

将设置指定为 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
你应该添加配置部分,配置会是什么样子? - Demodave

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进行调试) - Emanuel Vintilă

0

我认为你关于序列化设置的想法是正确的。请参考我的回答,里面有一个示例:

两个应用程序之间共享配置的技巧?

你可以创建一个数组属性,像这样:

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

0
编写一些函数,将整数数组转换为字符串,但在每个数字之间加入一个字符,例如空格。
因此,如果数组是{1,34,546,56},则字符串应该是"1 34 645 56"。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接