Jak przechowywać tablicę int [] w ustawieniach aplikacji


93

Tworzę prostą aplikację Windows Forms przy użyciu C # express 2008. Jestem doświadczonym programistą C ++, ale jestem prawie zupełnie nowy w C # i .NET.

Obecnie przechowuję niektóre z moich prostych ustawień aplikacji przy użyciu projektanta ustawień i kodu w ten sposób:

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

Teraz chciałbym zapisać albo tablicę ints ( int[]), albo prawdopodobnie Listę ints ( List< int >), jako ustawienie. Jednak nie wiem, jak to zrobić. Przeszukałem dokumentację, stackoverflow i google i nie mogę znaleźć przyzwoitego wyjaśnienia, jak to zrobić.

Moje przeczucie oparte na nielicznych przykładach, które znalazłem, jest takie, że muszę utworzyć klasę, którą można serializować, która otacza moją tablicę lub listę, a następnie będę mógł użyć tego typu w projektancie ustawień. Jednak nie jestem pewien, jak to zrobić.

Odpowiedzi:


137

Jest też inne rozwiązanie - wymaga trochę ręcznej edycji pliku ustawień, ale potem działa dobrze w środowisku VS i w kodzie. I nie wymaga żadnych dodatkowych funkcji ani opakowań.

Chodzi o to, że VS pozwala int[]domyślnie serializować typ w pliku ustawień - po prostu domyślnie nie pozwala ci go wybrać. Stwórz więc ustawienie o żądanej nazwie (np. SomeTestSetting) i nadaj mu dowolny typ (np. stringDomyślnie). Zapisz zmiany.

Teraz przejdź do folderu projektu i otwórz plik „Właściwości \ Ustawienia.settings” za pomocą edytora tekstu (na przykład Notatnika). Możesz też otworzyć go w programie VS, klikając prawym przyciskiem myszy w Eksploratorze rozwiązań na „-> Właściwości -> Ustawienia.settings” ”, wybierz„ Otwórz za pomocą ... ”, a następnie wybierz„ Edytor XML ”lub„ Edytor kodu źródłowego (tekstu) ”. W otwartych ustawieniach XML znajdź swoje ustawienie (będzie wyglądać tak):

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

Zmień parametr „Typ” z System.Stringna System.Int32[]. Teraz ta sekcja będzie wyglądać następująco:

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

Teraz zapisz zmiany i ponownie otwórz ustawienia projektu - voilà! - Mamy ustawienie SomeTestSetting z typem, do System.Int32[]którego można uzyskać dostęp i edytować go za pomocą VS Settings Designer (również wartości), a także w kodzie.


5
Niesamowite. Aby wprowadzić coś w ustawieniach za pomocą edytora w Visual Studio, powinieneś wkleić coś takiego, to jest dla tablicy ciągów, której potrzebowałem <? 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 .. nie rozumiem, dlaczego ta odpowiedź nie została zaakceptowana .. nie jest wymagany dodatkowy kod (wystarczy zmodyfikować jedną linię istniejącego xml) i otrzymujesz bezpieczeństwo typów i pełne wsparcie dla projektantów!
Chris

1
Chris, dzięki :) Rzecz w tym, że dodałem swoją odpowiedź znacznie później niż pierwotnie zaakceptowana odpowiedź (właściwie około rok później :)). Tylko dzieląc się doświadczeniem :)
Jen-Ari

6
Dla każdego, kto jest ciekawy, składnia XML pliku konfiguracyjnego dla int[]wyglądałaby tak (z wyjątkiem ładnie wydrukowanego):<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
Zgoda, to lepsza odpowiedź, więc zmieniłem ocenę.
sidewinderguy

41

przechować:

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

odtworzyć:

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

Edycja: sugestia Abela!


Ok, więc wygląda na to, że ręcznie serializujesz dane do łańcucha i odwrotnie. Myślę, że to zadziała, mógłbym po prostu zapisać dane jako ustawienie ciągu. Myślałem o zrobieniu czegoś takiego, ale szukałem czegoś bardziej „czystego”, „standardowego” lub po prostu bardziej .NET'owego. Będę o tym pamiętać, jeśli nie pojawi się nic lepszego. Dzięki.
sidewinderguy

Cześć, poszedłbym wtedy z aleją McKaya! (sekcja konfiguracji / grupa sekcji)
Mike Gleason jr Couturier

Zamierzam to zrobić, ponieważ jest to proste. Dziękuję wszystkim za wszystkie pomysły, jestem pewien, że pomogą w dalszej drodze.
sidewinderguy

2
Uwaga: aby System.Linqpowyższa sztuczka zadziałała, musisz dodać do swoich zastosowań / importów.
Sean Hanley

11

Jest jeszcze jeden sposób na osiągnięcie tego wyniku, który jest dużo czystszy w użyciu, ale wymaga więcej kodu. Mój implementujący niestandardowy typ i konwerter typów jest możliwy następujący kod:

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

Aby to osiągnąć, potrzebujesz typu z konwerterem typów, który umożliwia konwersję do iz ciągów. Robisz to, dekorując typ za pomocą TypeConverterAttribute:

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

Następnie implementujemy ten konwerter typu jako wyprowadzenie 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);
    }
}

Zapewniając kilka wygodnych metod w klasie MyNumberArray, którą możemy następnie bezpiecznie przypisać do iz listy, cała klasa wyglądałaby mniej więcej tak:

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

Na koniec, aby użyć tego w ustawieniach, dodajesz powyższe klasy do zestawu i kompilujesz. W edytorze Settings.settings wystarczy kliknąć opcję „Browse”, wybrać klasę MyNumberArray i gotowe.

Znowu jest to dużo więcej kodu; Jednak można go zastosować do znacznie bardziej skomplikowanych typów danych niż prosta tablica.


Dzięki temu wygląda to ciekawie. Spróbuję, kiedy będę miał okazję.
sidewinderguy

3

Określ ustawienie jako System.Collections.ArrayList, a następnie:

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

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

2

Prostym rozwiązaniem jest ustawienie wartości domyślnej ustawienia na null we właściwości, ale w konstruktorze sprawdź, czy właściwość ma wartość null, a jeśli tak, ustaw ją na rzeczywistą wartość domyślną. Więc jeśli chcesz mieć tablicę liczb 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;
        }
    }
}

Wydaje się trochę hacky, ale jest czysty i działa zgodnie z oczekiwaniami, ponieważ właściwości są inicjowane do ich ostatnich (lub domyślnych) ustawień przed wywołaniem konstruktora.


2
Projektant plików ustawień aplikacji automatycznie generuje kod, a więc taka zmiana zostanie nadpisana za każdym razem, gdy ktoś użyje projektanta, nawet przypadkowo.
Carl G,

1

Używany System.Object.

Przykład:

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

Wyciąg:

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

1
Próbowałem użyć System.Object, ale ustawienia nie były zachowywane między sesjami. (Tak, to była kompilacja wydania, samodzielna instalacja, jakkolwiek chcesz to nazwać, nie debugowałem za pomocą IDE)
Emanuel Vintilă


0

Stwórz kilka funkcji, które konwertują tablicę int w łańcuch, ale między nimi wstaw znak taki jak "" (spacja).

Więc jeśli tablicą jest {1,34,546,56}, ciąg będzie miał postać „1 34 645 56”

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.