วิธีจัดเก็บอาร์เรย์ int [] ในการตั้งค่าแอปพลิเคชัน


93

ฉันกำลังสร้างแอปพลิเคชัน Windows Forms แบบธรรมดาโดยใช้ C # express 2008 ฉันเป็นนักพัฒนา C ++ ที่มีประสบการณ์ แต่ฉันค่อนข้างใหม่สำหรับ C # และ. NET

ขณะนี้ฉันกำลังจัดเก็บการตั้งค่าแอปพลิเคชันง่ายๆของฉันโดยใช้ตัวออกแบบการตั้งค่าและรหัสดังนี้:

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

ตอนนี้ฉันต้องการเก็บอาร์เรย์ของ ints ( int[]) หรืออาจจะเป็น List of ints ( List< int >) เป็นค่าติดตั้ง อย่างไรก็ตามฉันคิดไม่ออกว่าจะทำอย่างไร ฉันได้ค้นหาเอกสาร, stackoverflow และ google แล้วและฉันไม่พบคำอธิบายที่เหมาะสมเกี่ยวกับวิธีการทำเช่นนี้

ลางสังหรณ์ของฉันขึ้นอยู่กับตัวอย่างที่กระจัดกระจายที่ฉันพบคือฉันต้องสร้างคลาสที่สามารถต่ออนุกรมกันได้ที่ล้อมอาร์เรย์หรือรายการของฉันแล้วฉันจะสามารถใช้ Type นั้นในตัวออกแบบการตั้งค่าได้ อย่างไรก็ตามฉันไม่แน่ใจว่าต้องทำอย่างไร

คำตอบ:


137

นอกจากนี้ยังมีวิธีแก้ปัญหาอื่น - ต้องใช้การแก้ไขไฟล์การตั้งค่าด้วยตนเองเล็กน้อย แต่หลังจากนั้นทำงานได้ดีในสภาพแวดล้อม VS และในโค้ด และไม่ต้องใช้ฟังก์ชันหรือเครื่องห่อเพิ่มเติม

สิ่งนี้คือ VS อนุญาตให้จัดint[]ประเภทตามค่าเริ่มต้นในไฟล์การตั้งค่า - มันไม่อนุญาตให้คุณเลือกโดยค่าเริ่มต้น ดังนั้นสร้างการตั้งค่าด้วยชื่อที่ต้องการ (เช่น SomeTestSetting) และตั้งเป็นประเภทใดก็ได้ (เช่นstringโดยค่าเริ่มต้น) บันทึกการเปลี่ยนแปลง

ตอนนี้ไปที่โฟลเดอร์โครงการของคุณและเปิดไฟล์ "Properties \ Settings.settings" ด้วยโปรแกรมแก้ไขข้อความ (เช่น Notepad) หรือคุณสามารถเปิดใน VS โดยคลิกขวาใน Solution Explorer ที่ "-> Properties -> Settings.settings ", เลือก" Open With ... "จากนั้นเลือก" XML Editor "หรือ" Source Code (Text) Editor " ในการตั้งค่า xml ที่เปิดให้ค้นหาการตั้งค่าของคุณ (จะมีลักษณะดังนี้):

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

เปลี่ยน "ประเภท" param จากไปSystem.String System.Int32[]ตอนนี้ส่วนนี้จะมีลักษณะดังนี้:

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

บันทึกการเปลี่ยนแปลงและเปิดการตั้งค่าโครงการอีกครั้ง - voila! - เรามีการตั้งค่า SomeTestSetting ด้วย type System.Int32[]ซึ่งสามารถเข้าถึงและแก้ไขได้ผ่าน VS Settings Designer (ค่าด้วย) เช่นเดียวกับในโค้ด


5
น่ากลัว หากต้องการป้อนบางสิ่งในการตั้งค่าโดยใช้ตัวแก้ไขใน Visual Studio คุณควรวางสิ่งนี้สำหรับอาร์เรย์สตริงซึ่งเป็นสิ่งที่ฉันต้องการ <? 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 .. ไม่เข้าใจว่าทำไมจึงไม่ยอมรับคำตอบ .. ไม่ต้องใช้รหัสพิเศษ (แก้ไข xml ที่มีอยู่เพียงบรรทัดเดียว) และคุณจะได้รับความปลอดภัยและการสนับสนุนแบบเต็มเทียบกับนักออกแบบ!
คริส

1
คริสขอบคุณ :) สิ่งนี้คือฉันได้เพิ่มคำตอบของฉันในภายหลังจากนั้นคำตอบที่ยอมรับในตอนแรก (ประมาณหนึ่งปีต่อมาจริง ๆ :)) แค่แชร์ประสบการณ์ :)
เจน - อารีย์

6
สำหรับใครที่อยากรู้อยากเห็นไวยากรณ์ XML ของไฟล์ config int[]จะมีลักษณะเช่นนี้ (ยกเว้นพิมพ์สวย):<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

42

ในการจัดเก็บ:

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

เพื่อสร้างใหม่:

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

แก้ไข: คำแนะนำของ Abel!


ตกลงดูเหมือนว่าคุณกำลังต่ออนุกรมข้อมูลด้วยตนเองเป็นสตริงและในทางกลับกัน ฉันเดาว่ามันจะได้ผลฉันแค่เก็บข้อมูลเป็นการตั้งค่าสตริง ฉันคิดจะทำอะไรแบบนี้ แต่ฉันกำลังมองหาสิ่งที่ "สะอาด" หรือ "มาตรฐาน" หรือมากกว่านั้น NET'ish ฉันจะจำไว้ว่าถ้าไม่มีอะไรดีขึ้น ขอบคุณ.
sidewinderguy

สวัสดีฉันจะไปกับถนนของ McKay แล้ว! (ส่วนการกำหนดค่า / กลุ่มส่วน)
Mike Gleason jr Couturier

ฉันจะไปกับสิ่งนี้เพราะมันเรียบง่าย ขอบคุณสำหรับความคิดทั้งหมดของทุกคนฉันมั่นใจว่าพวกเขาจะช่วยได้
sidewinderguy

2
หมายเหตุ: คุณจะต้องSystem.Linqเพิ่มการใช้งาน / การนำเข้าของคุณเพื่อให้เคล็ดลับข้างต้นทำงานได้
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 จากนั้นเราสามารถกำหนดเข้าและออกจากรายการได้อย่างปลอดภัยคลาสที่สมบูรณ์จะมีลักษณะดังนี้:

[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

3

ระบุการตั้งค่าเป็น System.Collections.ArrayList แล้ว:

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

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

2

วิธีแก้ไขง่ายๆคือตั้งค่าเริ่มต้นของการตั้งค่าเป็น null ในคุณสมบัติ แต่ในตัวสร้างให้ตรวจสอบว่าคุณสมบัติเป็นโมฆะหรือไม่และถ้าเป็นเช่นนั้นให้ตั้งค่าเป็นค่าเริ่มต้นที่แท้จริง ดังนั้นหากคุณต้องการอาร์เรย์ของ 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;
        }
    }
}

รู้สึกเหมือนแฮ็ก แต่สะอาดและทำงานได้ตามที่ต้องการเนื่องจากคุณสมบัติถูกเริ่มต้นเป็นการตั้งค่าสุดท้าย (หรือค่าเริ่มต้น) ก่อนที่จะเรียกตัวสร้าง


2
ตัวออกแบบสำหรับไฟล์การตั้งค่าแอปพลิเคชันจะสร้างโค้ดโดยอัตโนมัติดังนั้นการเปลี่ยนแปลงเช่นนี้จะถูกเขียนทับเมื่อใดก็ตามที่ใครก็ตามที่ใช้ตัวออกแบบนี้โดยไม่ได้ตั้งใจ
Carl G

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

ฉันคิดว่าคุณพูดถูกเกี่ยวกับการกำหนดลำดับการตั้งค่าของคุณ ดูคำตอบของฉันสำหรับคำถามนี้สำหรับตัวอย่าง:

เทคนิคการแชร์ config ระหว่างสองแอพ?

คุณจะมีคุณสมบัติที่เป็นอาร์เรย์ดังนี้:

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

0

สร้างฟังก์ชันบางอย่างที่แปลงอาร์เรย์ int ในสตริง แต่ระหว่างแต่ละฟังก์ชันใส่อักขระเช่น "" (ช่องว่าง)

ดังนั้นหากอาร์เรย์เป็น {1,34,546,56} สตริงจะเป็น "1 34 645 56"

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.