เหตุใดฉันจึงสามารถเริ่มรายการเหมือนอาร์เรย์ใน C # ได้


131

วันนี้ฉันประหลาดใจที่พบว่าใน C # ฉันสามารถทำได้:

List<int> a = new List<int> { 1, 2, 3 };

ทำไมฉันถึงทำได้ ตัวสร้างเรียกว่าอะไร? ฉันจะทำสิ่งนี้กับชั้นเรียนของตัวเองได้อย่างไร ฉันรู้ว่านี่เป็นวิธีเริ่มต้นอาร์เรย์ แต่อาร์เรย์เป็นรายการภาษาและรายการเป็นวัตถุง่ายๆ ...


1
คำถามนี้อาจช่วยได้: stackoverflow.com/questions/1744967/…
Bob Kaufman

10
น่ากลัวใช่มั้ย? คุณสามารถสร้างรหัสที่คล้ายกันมากเพื่อเริ่มต้นพจนานุกรมได้เช่นกัน:{ { "key1", "value1"}, { "key2", "value2"} }
danludwig

จากมุมมองอื่นอาร์เรย์นี้เช่นไวยากรณ์การเริ่มต้นช่วยเตือนเราว่าประเภทข้อมูลพื้นฐานของ a List<int>เป็นอาร์เรย์เท่านั้น ฉันเกลียดทีม C # ที่พวกเขาไม่ได้ตั้งชื่อArrayList<T>ที่ฟังดูชัดเจนและเป็นธรรมชาติ
RBT

คำตอบ:


183

นี่เป็นส่วนหนึ่งของไวยากรณ์ของ collection initializer ใน. NET คุณสามารถใช้ไวยากรณ์นี้กับคอลเลกชันใดก็ได้ที่คุณสร้างตราบใดที่:

  • มันดำเนินการIEnumerable(โดยเฉพาะอย่างยิ่งIEnumerable<T>)

  • มันมีเมธอดชื่อ Add(...)

สิ่งที่เกิดขึ้นคือตัวสร้างเริ่มต้นถูกเรียกและจากนั้นAdd(...)จะถูกเรียกสำหรับสมาชิกแต่ละคนของ initializer

ดังนั้นสองบล็อกนี้จึงเหมือนกันโดยประมาณ:

List<int> a = new List<int> { 1, 2, 3 };

และ

List<int> temp = new List<int>();
temp.Add(1);
temp.Add(2);
temp.Add(3);
List<int> a = temp;

คุณสามารถเรียกตัวสร้างทางเลือกได้หากคุณต้องการตัวอย่างเช่นเพื่อป้องกันการปรับขนาดมากเกินไปในList<T>ระหว่างการเจริญเติบโตเป็นต้น:

// Notice, calls the List constructor that takes an int arg
// for initial capacity, then Add()'s three items.
List<int> a = new List<int>(3) { 1, 2, 3, }

โปรดทราบว่าAdd()วิธีนี้ไม่จำเป็นต้องใช้รายการเดียวตัวอย่างเช่นAdd()วิธีการDictionary<TKey, TValue>รับสองรายการ:

var grades = new Dictionary<string, int>
    {
        { "Suzy", 100 },
        { "David", 98 },
        { "Karen", 73 }
    };

คล้ายกับ:

var temp = new Dictionary<string, int>();
temp.Add("Suzy", 100);
temp.Add("David", 98);
temp.Add("Karen", 73);
var grades = temp;

ดังนั้นในการเพิ่มสิ่งนี้ในชั้นเรียนของคุณเองสิ่งที่คุณต้องทำตามที่กล่าวไว้คือใช้งานIEnumerable(อีกครั้งโดยเฉพาะอย่างยิ่งIEnumerable<T>) และสร้างวิธีการอย่างน้อยหนึ่งAdd()วิธี:

public class SomeCollection<T> : IEnumerable<T>
{
    // implement Add() methods appropriate for your collection
    public void Add(T item)
    {
        // your add logic    
    }

    // implement your enumerators for IEnumerable<T> (and IEnumerable)
    public IEnumerator<T> GetEnumerator()
    {
        // your implementation
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

จากนั้นคุณสามารถใช้งานได้เหมือนกับที่คอลเลกชัน BCL ทำ:

public class MyProgram
{
    private SomeCollection<int> _myCollection = new SomeCollection<int> { 13, 5, 7 };    

    // ...
}

(สำหรับข้อมูลเพิ่มเติมโปรดดูMSDN )


29
คำตอบที่ดีแม้ว่าฉันจะทราบว่าการพูดว่า "เหมือนกันทุกประการ" ในตัวอย่างแรกของคุณไม่ถูกต้องนัก มันเป็นสิ่งที่เหมือนกันกับList<int> temp = new List<int>(); temp.Add(1); ... List<int> a = temp; ที่เป็นaตัวแปรที่ไม่ได้เริ่มต้นจนกระทั่งหลังจากที่ทั้งหมดที่เพิ่มจะเรียกว่า มิฉะนั้นจะถูกกฎหมายหากทำสิ่งList<int> a = new List<int>() { a.Count, a.Count, a.Count };ที่เป็นเรื่องบ้าๆ
Eric Lippert

1
@ user606723: ไม่มีความแตกต่างที่แท้จริงระหว่างList<int> a; a = new List<int>() { a.Count };และList<int> a = new List<int>() { a.Count };
Joren

4
@Joren: คุณถูกต้อง; ในความเป็นจริงข้อกำหนด C # ระบุว่าT x = y;เหมือนกับT x; x = y;ข้อเท็จจริงนี้อาจนำไปสู่สถานการณ์แปลก ๆ ตัวอย่างเช่นint x = M(out x) + x;ถูกกฎหมายอย่างสมบูรณ์เพราะint x; x = M(out x) + x;ถูกกฎหมาย
Eric Lippert

1
@JamesMichaelHare ไม่จำเป็นต้องใช้IEnumerable<T>; non-generic IEnumerableนั้นเพียงพอที่จะอนุญาตให้ใช้ไวยากรณ์ collection initializer
กลัว

2
ประเด็นของ Eric มีความสำคัญหากคุณกำลังใช้คอลเลคชันบางประเภทที่ใช้ IDisposable using (var x = new Something{ 1, 2 })จะไม่ทิ้งวัตถุหากการAddเรียกหนึ่งล้มเหลว
porges

11

มันถูกเรียกว่าน้ำตาลประโยค

List<T> เป็นคลาส "ง่าย" แต่คอมไพเลอร์ให้การดูแลเป็นพิเศษเพื่อให้ชีวิตของคุณง่ายขึ้น

หนึ่งนี้จะเรียกว่าคอลเลกชัน initializer คุณต้องใช้IEnumerable<T>และAddวิธีการ


8

ตามข้อกำหนดC # เวอร์ชัน 3.0 "วัตถุคอลเลกชันที่ใช้เริ่มต้นการรวบรวมต้องเป็นประเภทที่ใช้ System.Collections.Generic.ICollection สำหรับหนึ่ง T. "

อย่างไรก็ตามข้อมูลนี้ดูเหมือนจะไม่ถูกต้องในขณะที่เขียนนี้ ดูคำชี้แจงของ Eric Lippert ในความคิดเห็นด้านล่าง


10
หน้านั้นไม่แม่นยำ ขอขอบคุณที่แจ้งเรื่องนี้ให้ฉันทราบ นั่นต้องเป็นเอกสารก่อนวางจำหน่ายเบื้องต้นที่ไม่เคยถูกลบทิ้ง การออกแบบดั้งเดิมนั้นต้องการ ICollection แต่นั่นไม่ใช่การออกแบบขั้นสุดท้ายที่เราตัดสิน
Eric Lippert

1
ขอบคุณสำหรับคำชี้แจง
Olivier Jacot-Descombes


6

สิ่งที่ยอดเยี่ยมอีกอย่างเกี่ยวกับการเริ่มต้นการรวบรวมคือคุณสามารถมีAddวิธีการที่มากเกินไปหลายวิธีและคุณสามารถเรียกใช้ทั้งหมดได้ในตัวเริ่มต้นเดียวกัน! ตัวอย่างเช่นการทำงานนี้:

public class MyCollection<T> : IEnumerable<T>
{
    public void Add(T item, int number)
    {

    }
    public void Add(T item, string text) 
    {

    }
    public bool Add(T item) //return type could be anything
    {

    }
}

var myCollection = new MyCollection<bool> 
{
    true,
    { false, 0 },
    { true, "" },
    false
};

มันเรียกโอเวอร์โหลดที่ถูกต้อง นอกจากนี้ยังมองหาเพียงวิธีการที่มีชื่อAddประเภทการส่งคืนอาจเป็นอะไรก็ได้


0

อาร์เรย์เช่นไวยากรณ์กำลังถูกส่งในชุดการAdd()โทร

หากต้องการดูสิ่งนี้ในตัวอย่างที่น่าสนใจมากขึ้นให้พิจารณาโค้ดต่อไปนี้ซึ่งฉันทำสิ่งที่น่าสนใจสองอย่างซึ่งฟังดูผิดกฎหมายประการแรกใน C # 1) การตั้งค่าคุณสมบัติแบบอ่านอย่างเดียว 2) การตั้งค่ารายการด้วยอาร์เรย์เช่น initializer

public class MyClass
{   
    public MyClass()
    {   
        _list = new List<string>();
    }
    private IList<string> _list;
    public IList<string> MyList 
    { 
        get
        { 
            return _list;
        }
    }
}
//In some other method
var sample = new MyClass
{
    MyList = {"a", "b"}
};

รหัสนี้จะทำงานได้อย่างสมบูรณ์แม้ว่า 1) MyList เป็นแบบอ่านอย่างเดียวและ 2) ฉันตั้งค่ารายการด้วยตัวเริ่มต้นอาร์เรย์

สาเหตุที่ใช้งานได้เนื่องจากในโค้ดที่เป็นส่วนหนึ่งของอ็อบเจ็กต์อินเทียไลเซอร์คอมไพเลอร์จะเปลี่ยน{}ไวยากรณ์ที่คล้ายกันเป็นชุดของการAdd()เรียกที่ถูกกฎหมายอย่างสมบูรณ์แม้ในฟิลด์แบบอ่านอย่างเดียว

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