Cómo almacenar la matriz int [] en la configuración de la aplicación

93

Estoy creando una aplicación simple de Windows Forms usando C # express 2008. Soy un desarrollador experimentado de C ++, pero soy bastante nuevo en C # y .NET.

Actualmente estoy almacenando algunas de las configuraciones de mi aplicación simple usando el diseñador de configuraciones y un código como este:

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

Ahora me gustaría almacenar una matriz de ints ( int[]), o posiblemente una Lista de ints ( List< int >), como configuración. Sin embargo, no puedo averiguar cómo hacer esto. He buscado en la documentación, stackoverflow y google, y no puedo encontrar una explicación decente de cómo hacer esto.

Mi corazonada basada en los ejemplos escasos que he encontrado es que tengo que crear una clase que sea serializable que envuelva mi matriz o Lista, y luego podré usar ese Tipo en el diseñador de configuraciones. Sin embargo, no estoy seguro de cómo hacer esto exactamente.

sidewinderguy
fuente

Respuestas:

137

También hay otra solución: requiere un poco de edición manual del archivo de configuración, pero luego funciona bien en el entorno VS y en el código. Y no requiere funciones ni envoltorios adicionales.

El caso es que VS permite serializar el int[]tipo de forma predeterminada en el archivo de configuración, simplemente no le permite seleccionarlo de forma predeterminada. Por lo tanto, cree una configuración con el nombre deseado (por ejemplo, SomeTestSetting) y hágalo de cualquier tipo (por ejemplo,string por por defecto). Guarde los cambios.

Ahora vaya a la carpeta de su proyecto y abra el archivo "Properties \ Settings.settings" con el editor de texto (Bloc de notas, por ejemplo) O puede abrirlo en VS haciendo clic con el botón derecho en el Explorador de soluciones en "-> Propiedades -> Configuración. ", seleccione" Abrir con ... "y luego elija" Editor XML "o" Editor de código fuente (texto) ". En la configuración xml abierta, busque su configuración (se verá así):

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

Cambie el parámetro "Tipo" de System.Stringa System.Int32[]. Ahora esta sección se verá así:

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

Ahora guarde los cambios y vuelva a abrir la configuración del proyecto - ¡voilà! - Tenemos la configuración SomeTestSetting con tipo a la System.Int32[]que se puede acceder y editar a través de VS Settings Designer (valores también), así como en el código.

Jen-Ari
fuente
5
Increíble. Para ingresar algo en la configuración usando el editor en Visual Studio, debe pegar algo como esto, esto es para una matriz de cadenas, que era lo que necesitaba <? 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 ... no entiendo por qué no se aceptó esta respuesta ... no se requiere código adicional (solo modifique una línea del xml existente) y obtendrá seguridad de tipos y soporte completo frente al diseñador.
Chris
1
Chris, gracias :) El caso es que agregué mi respuesta mucho más tarde que la respuesta originalmente aceptada (aproximadamente un año después, en realidad :)). Sin embargo, solo comparto la experiencia :)
Jen-Ari
6
Para cualquier persona curiosa, la sintaxis XML del archivo de configuración int[]se vería así (excepto que está bastante impresa):<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
De acuerdo, esta es una mejor respuesta, así que cambié la marca.
sidewinderguy
40

Almacenar:

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

para recrear:

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

Editar: ¡sugerencia de Abel!

Mike Gleason jr modisto
fuente
De acuerdo, parece que está serializando los datos manualmente en una cadena, y viceversa. Supongo que esto funcionaría, podría almacenar los datos como una configuración de cadena. Pensé en hacer algo como esto, pero estaba buscando algo más "limpio" o "estándar" o simplemente más .NET'ish. Sin embargo, tendré esto en cuenta, si no surge nada mejor. Gracias.
sidewinderguy
Hola, entonces me iría con McKay's Avenue! (sección de configuración / grupo de sección)
Mike Gleason jr Couturier
Voy a seguir con esto porque es simple. Gracias por todas las ideas a todos, estoy seguro de que ayudarán en el futuro.
sidewinderguy
2
Nota: Deberá haber System.Linqagregado a sus usos / importaciones para que funcione el truco anterior.
Sean Hanley
11

Hay otra forma de lograr este resultado que es mucho más limpia en el uso pero requiere más código. Al implementar un tipo personalizado y un convertidor de tipos, el siguiente código es posible:

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

Para lograr esto, necesita un tipo con un convertidor de tipos que permita la conversión hacia y desde cadenas. Para ello, decora el tipo con TypeConverterAttribute:

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

Luego, implementando este convertidor de tipos como una derivación de 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);
    }
}

Al proporcionar algunos métodos de conveniencia en la clase MyNumberArray, podemos asignar de manera segura ay desde List, la clase completa se vería así:

[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(); }
}

Por último, para usar esto en la configuración, agregue las clases anteriores a un ensamblado y compile. En su editor Settings.settings, simplemente haga clic en la opción "Examinar" y seleccione la clase MyNumberArray y listo.

Nuevamente, esto es mucho más código; sin embargo, se puede aplicar a tipos de datos mucho más complicados que un arreglo simple.

csharptest.net
fuente
Gracias, esto parece interesante. Lo probaré cuando tenga la oportunidad.
sidewinderguy
3

Especifique la configuración como System.Collections.ArrayList y luego:

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

int[] array = (int[])Settings.Default.IntArray.ToArray(typeof(int));
João Angelo
fuente
2

Una solución simple es establecer el valor predeterminado de una configuración en nulo en la propiedad, pero en el constructor verifique si la propiedad es nula y, si es así, configúrelo en su valor predeterminado real. Entonces, si quisieras una variedad de entradas:

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;
        }
    }
}

Se siente un poco hacky, pero está limpio y funciona como se desea, ya que las propiedades se inicializan a su última configuración (o predeterminada) antes de que se llame al constructor.

Russell Bearden
fuente
2
El diseñador de archivos de configuración de la aplicación genera automáticamente el código, por lo que un cambio como este se sobrescribirá cada vez que alguien use el diseñador, incluso accidentalmente.
Carl G
1

Usado System.Object.

Ejemplo:

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

Extraer:

arBytes = (byte[])Properties.Settings.Default.KeyObject;
Eslavos
fuente
1
Intenté usar System.Object, pero la configuración no persistía entre sesiones. (Sí, era una versión de lanzamiento, una instalación independiente, como quieras llamarlo, no estaba depurando usando el IDE)
Emanuel Vintilă
0

Creo que tienes razón al serializar tu configuración. Vea mi respuesta a esta pregunta para obtener una muestra:

¿Técnicas para compartir una configuración entre dos aplicaciones?

Tendría una propiedad que es una matriz, como esta:

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

Haga algunas funciones que conviertan una matriz int en una cadena, pero entre cada una coloque un carácter como "" (espacio).

Entonces, si la matriz es {1,34,546,56}, la cadena sería "1 34645 56"

usuario1452079
fuente