Comment stocker le tableau int [] dans les paramètres de l'application

93

Je crée une application Windows Forms simple en utilisant C # express 2008. Je suis un développeur C ++ expérimenté, mais je suis à peu près tout nouveau dans C # et .NET.

Je stocke actuellement certains de mes paramètres d'application simples à l'aide du concepteur de paramètres et d'un code comme celui-ci:

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

Maintenant, j'aimerais stocker soit un tableau de ints ( int[]), soit éventuellement une liste d'ints ( List< int >), en tant que paramètre. Cependant, je ne peux pas comprendre comment faire cela. J'ai recherché la documentation, le stackoverflow et Google, et je ne trouve pas d'explication décente sur la façon de procéder.

Mon intuition basée sur les exemples rares que j'ai trouvés est que je dois créer une classe sérialisable qui encapsule mon tableau ou ma liste, puis je pourrai utiliser ce type dans le concepteur de paramètres. Cependant, je ne sais pas exactement comment procéder.

sidewinderguy
la source

Réponses:

137

Il existe également une autre solution - nécessite un peu d'édition manuelle du fichier de paramètres, mais fonctionne ensuite très bien dans l'environnement VS et dans le code. Et ne nécessite aucune fonction ou emballage supplémentaire.

Le fait est que VS permet de sérialiser le int[]type par défaut dans le fichier de paramètres - il ne vous permet tout simplement pas de le sélectionner par défaut. Alors, créez un paramètre avec le nom souhaité (par exemple SomeTestSetting) et faites-le de n'importe quel type ( stringpar exemple par défaut). Enregistrez les modifications.

Maintenant, allez dans votre dossier de projet et ouvrez le fichier "Properties \ Settings.settings" avec l'éditeur de texte (Notepad, par exemple) Ou vous pouvez l'ouvrir dans VS en cliquant avec le bouton droit dans l'Explorateur de solutions sur "-> Propriétés -> Settings.settings ", sélectionnez" Ouvrir avec ... ", puis choisissez" Editeur XML "ou" Editeur de code source (texte) ". Dans les paramètres xml ouverts, trouvez votre paramètre (il ressemblera à ceci):

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

Modifiez le paramètre "Type" de System.Stringà System.Int32[]. Maintenant, cette section ressemblera à ceci:

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

Maintenant, enregistrez les modifications et rouvrez les paramètres du projet - voilà! - Nous avons le paramètre SomeTestSetting avec type System.Int32[]qui peut être consulté et modifié via VS Settings Designer (valeurs aussi), ainsi que dans le code.

Jen-Ari
la source
5
Impressionnant. Pour entrer quelque chose dans le paramètre à l'aide de l'éditeur de Visual Studio, vous devez coller quelque chose comme ceci, c'est pour un tableau de chaînes, ce dont j'avais besoin <? 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 .. je ne comprends pas pourquoi cette réponse n'a pas été acceptée .. aucun code supplémentaire requis (il suffit de modifier une ligne de xml existant) et vous obtenez la sécurité de type et le support complet contre le concepteur!
Chris
1
Chris, merci :) Le fait est que j'ai ajouté ma réponse beaucoup plus tard que la réponse initialement acceptée (environ un an plus tard, en fait :)). Je ne fais que partager l'expérience :)
Jen-Ari
6
Pour toute personne curieuse, la syntaxe XML du fichier de configuration pour int[]ressemblerait à ceci (sauf assez imprimée):<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
D'accord, c'est une meilleure réponse, alors j'ai changé la marque.
sidewinderguy
40

ranger:

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

pour recréer:

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

Edit: suggestion d'Abel!

Mike Gleason jr Couturier
la source
Ok, il semble que vous sérialisiez les données manuellement dans une chaîne, et vice-versa. Je suppose que cela fonctionnerait, je pourrais simplement stocker les données sous forme de paramètre de chaîne. J'ai pensé faire quelque chose comme ça, mais je cherchais quelque chose de plus "propre" ou "standard" ou juste plus .NET'ish. Je garderai cela à l'esprit, si rien de mieux ne se présente. Merci.
sidewinderguy
Salut, j'irais avec McKay's avenue alors! (section de configuration / groupe de sections)
Mike Gleason jr Couturier
Je vais y aller parce que c'est simple. Merci pour toutes les idées à tous, je suis sûr qu'elles vous aideront plus tard.
sidewinderguy
2
Remarque: vous devrez avoir System.Linqajouté à vos utilisations / importations pour que l'astuce ci-dessus fonctionne.
Sean Hanley
11

Il existe une autre façon d'obtenir ce résultat qui est beaucoup plus propre dans l'utilisation mais qui nécessite plus de code. Mon implémentation d'un type personnalisé et d'un convertisseur de type, le code suivant est possible:

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

Pour ce faire, vous avez besoin d'un type avec un convertisseur de type qui permet la conversion vers et à partir de chaînes. Vous faites cela en décorant le type avec le TypeConverterAttribute:

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

Ensuite, implémentation de ce convertisseur de type en tant que dérivation 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);
    }
}

En fournissant des méthodes pratiques sur la classe MyNumberArray, nous pouvons ensuite attribuer en toute sécurité à et à partir de List, la classe complète ressemblerait à quelque chose comme:

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

Enfin, pour l'utiliser dans les paramètres, vous ajoutez les classes ci-dessus à un assembly et compilez. Dans votre éditeur Settings.settings, cliquez simplement sur l'option "Parcourir" et sélectionnez la classe MyNumberArray et c'est parti.

Encore une fois, c'est beaucoup plus de code; cependant, il peut être appliqué à des types de données beaucoup plus compliqués qu'un simple tableau.

csharptest.net
la source
Merci, cela semble intéressant. J'essaierai quand j'en aurai l'occasion.
sidewinderguy
3

Spécifiez le paramètre en tant que System.Collections.ArrayList, puis:

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

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

Une solution simple consiste à définir la valeur par défaut d'un paramètre sur null dans la propriété, mais dans le constructeur, vérifiez si la propriété est nulle et, le cas échéant, définissez-la sur sa valeur par défaut réelle. Donc, si vous vouliez un tableau d'entiers:

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

Cela semble un peu piraté, mais c'est propre et fonctionne comme vous le souhaitez puisque les propriétés sont initialisées à leurs derniers paramètres (ou par défaut) avant que le constructeur ne soit appelé.

Russell Bearden
la source
2
Le concepteur des fichiers de paramètres d'application génère automatiquement le code, de sorte qu'un changement comme celui-ci serait écrasé chaque fois que quelqu'un utilisait le concepteur, même accidentellement.
Carl G du
1

Utilisé System.Object.

Exemple:

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

Extrait:

arBytes = (byte[])Properties.Settings.Default.KeyObject;
Slaves
la source
1
J'ai essayé d'utiliser System.Object, mais les paramètres ne persistaient pas entre les sessions. (Oui, c'était une version de version, une installation autonome, comme vous voulez l'appeler, je ne déboguais pas à l'aide de l'IDE)
Emanuel Vintilă
0

Je pense que vous avez raison de sérialiser vos paramètres. Voir ma réponse à cette question pour un exemple:

Techniques pour partager une configuration entre deux applications?

Vous auriez une propriété qui est un tableau, comme ceci:

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

Créez des fonctions qui convertissent un tableau int en une chaîne, mais entre chacune, placez un caractère comme "" (espace).

Donc, si le tableau est {1,34,546,56}, la chaîne serait "1 34 645 56"

user1452079
la source