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

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

Actualmente estoy almacenando algunas de las configuraciones simples de mi aplicación usando el diseñador de configuraciones y el 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 entradas ( int[] ), o posiblemente una Lista de entradas ( List ), como una configuración. Sin embargo, no puedo entender 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 escasos ejemplos 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 exactamente cómo hacer esto.

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 adicionales o envoltorios.

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

Ahora ve a la carpeta de tu proyecto y abre el archivo “Propiedades \ Configuracións.configuraciones” con el editor de texto (Bloc de notas, por ejemplo) O puedes abrirlo en VS haciendo clic con el botón derecho en el Explorador de soluciones en “-> Propiedades -> Configuraciones. “, seleccione” Abrir con … “y luego elija” Editor XML “o” Editor de código fuente (texto) “. En la configuración xml abierta, encuentre su configuración (se verá así):

    

Cambie el parámetro “Tipo” de System.String a System.Int32[] . Ahora esta sección se verá así:

    

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

Almacenar:

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

para volver a crear:

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

Editar: ¡sugerencia de Abel!

Hay otra forma de lograr este resultado que es mucho más limpia pero requiere más código. Mi implementación de un convertidor de tipo y tipo personalizado es posible con el siguiente código:

 List 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 tipo 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 tipo de convertidor 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 arr = new List(); 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 que luego podemos asignar de forma segura desde y hacia List, la clase completa se vería como:

 [TypeConverter(typeof(MyNumberArrayConverter))] public class MyNumberArray : IEnumerable { List _values; public MyNumberArray() { _values = new List(); } public MyNumberArray(IEnumerable values) { _values = new List(values); } public static implicit operator List(MyNumberArray arr) { return new List(arr._values); } public static implicit operator MyNumberArray(List values) { return new MyNumberArray(values); } public IEnumerator GetEnumerator() { return _values.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_values).GetEnumerator(); } } 

Por último, para usar esto en la configuración, agrega las clases anteriores a un ensamblaje y comstack. En el editor Settings.settings, simplemente haga clic en la opción “Buscar” y seleccione la clase MyNumberArray y listo.

De nuevo, esto es mucho más código; sin embargo, se puede aplicar a tipos de datos mucho más complicados que una matriz simple.

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

Una solución simple es establecer el valor predeterminado de una configuración para anular en la propiedad, pero en el constructor comprobar si la propiedad es nula y, en caso afirmativo, establecerlo en su valor predeterminado real. Entonces, si quisieras una variedad de ints:

 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 algo raro, pero está limpio y funciona como se desea, ya que las propiedades se inicializan a su última configuración (o predeterminada) antes de llamar al constructor.

Usado System.Object .

Ejemplo:

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

Extraer:

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

Creo que tienes razón sobre la serialización de tu configuración. Vea mi respuesta a esta pregunta para una muestra:

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

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

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

Realice algunas funciones que conviertan una matriz int en una cadena, pero entre cada una ponga un carácter como “” (espacio).

Entonces, si la matriz es {1,34,546,56} la cadena sería “1 34 645 56”