2012-01-13 52 views
127

Hôm nay tôi đã rất ngạc nhiên khi thấy rằng trong C# Tôi có thể làm:Tại sao tôi có thể khởi tạo một Danh sách như một mảng trong C#?

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

Tại sao tôi có thể làm điều này? Nhà xây dựng nào được gọi là? Làm thế nào tôi có thể làm điều này với các lớp học của riêng tôi? Tôi biết rằng đây là cách để khởi tạo mảng nhưng mảng là các mục ngôn ngữ và Danh sách là các đối tượng đơn giản ...

+1

Câu hỏi này có thể trợ giúp: http://stackoverflow.com/questions/1744967/in-c-is-there-a-way-to-write-custom-object-initializers-for-new-data- các loại –

+10

Khá tuyệt vời huh? Bạn cũng có thể thực hiện mã rất giống để khởi tạo từ điển: '{{" key1 "," value1 "}, {" key2 "," value2 "}}' – danludwig

+0

Từ một khung nhìn khác mảng này giống như cú pháp khởi tạo cho chúng ta một lời nhắc rằng kiểu dữ liệu của một 'Danh sách ' thực ra chỉ là một mảng. Tôi ghét đội C# vì thực tế là họ không đặt tên cho nó là 'ArrayList ' có vẻ rất rõ ràng và tự nhiên. – RBT

Trả lời

180

Đây là một phần của cú pháp khởi tạo bộ sưu tập trong .NET. Bạn có thể sử dụng cú pháp này trên bất kỳ bộ sưu tập bạn tạo miễn là:

  • Nó thực hiện IEnumerable (tốt nhất IEnumerable<T>)

  • Nó có một phương thức có tên Add(...)

gì sẽ xảy ra là constructor mặc định được gọi, và sau đó Add(...) được gọi cho mỗi thành viên của bộ khởi tạo.

Như vậy, hai khối này là xấp xỉ giống hệt nhau:

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; 

Bạn thể gọi một constructor thay thế nếu bạn muốn, ví dụ để ngăn chặn quá kích thước các List<T> trong đang phát triển, v.v ..:

// 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, } 

Lưu ý rằng phương pháp Add() không cần phải mất một mục duy nhất, ví dụ như phương pháp Add() cho Dictionary<TKey, TValue> mất hai mục:

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

là xấp xỉ giống với:

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

Vì vậy, để thêm video này của bạn lớp riêng, tất cả những gì bạn cần làm, như đã đề cập, là triển khai IEnumerable (một lần nữa, tốt nhất là IEnumerable<T>) và tạo một hoặc nhiều phương thức 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(); 
    } 
} 

Sau đó, bạn có thể sử dụng nó giống như những bộ sưu tập BCL làm:

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

    // ... 
} 

(Để biết thêm thông tin, vui lòng xem MSDN)

+0

Hah, cảm ơn :-) –

+29

Câu trả lời hay, mặc dù tôi lưu ý rằng không hoàn toàn chính xác khi nói "chính xác giống hệt" trong ví dụ đầu tiên của bạn. Nó giống hệt với Danh sách temp = new List (); temp.Add (1); ... Danh sách a = temp; 'Tức là, biến' a' không được khởi tạo cho đến * sau * tất cả số lần thêm được gọi. Nếu không, sẽ là hợp pháp khi làm một cái gì đó như 'List a = new List () {a.Count, a.Count, a.Count}; 'là một điều điên rồ để làm. –

+0

@EricLippert, 'Danh sách a = new List () {a.Count, a.Count, a.Count}; 'không hợp pháp trong cả hai trường hợp vì không có tham chiếu đến tại thời điểm đó. Tôi nghĩ đoạn mã ban đầu của anh ấy hoàn toàn ổn. – user606723

11

Ðược gọi như vậy syntactic sugar.

List<T> là lớp "đơn giản", nhưng trình biên dịch đưa ra một cách xử lý đặc biệt cho nó để làm cho cuộc sống của bạn dễ dàng hơn.

Cái này được gọi là collection initializer. Bạn cần triển khai phương thức IEnumerable<T>Add.

6

Nó hoạt động nhờ collection initializers mà về cơ bản yêu cầu bộ sưu tập để triển khai phương thức Thêm và sẽ thực hiện công việc cho bạn.

8

Theo C# Version 3.0 Specification "Đối tượng bộ sưu tập mà trình khởi tạo bộ sưu tập được áp dụng phải thuộc loại triển khai System.Collections.Generic.ICollection cho đúng một T."

Tuy nhiên, thông tin này có vẻ không chính xác như trong bài viết này; xem phần giải thích của Eric Lippert trong phần bình luận bên dưới.

+10

Trang đó không chính xác. Cảm ơn vì đã mang đến sự chú ý của tôi. Đó phải là một số tài liệu sơ bộ trước khi phát hành mà bằng cách nào đó không bao giờ bị xóa. Thiết kế ban đầu là yêu cầu ICollection, nhưng đó không phải là thiết kế cuối cùng mà chúng tôi đã giải quyết. –

+1

Cảm ơn bạn đã làm rõ. –

6

Một điều thú vị nữa về bộ khởi tạo bộ sưu tập là bạn có thể có nhiều quá tải phương thức Add và bạn có thể gọi tất cả chúng trong cùng một bộ khởi tạo! Ví dụ: hoạt động này:

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

Nó gọi quá tải chính xác. Ngoài ra, nó chỉ cho phương pháp với tên Add, kiểu trả về có thể là bất cứ thứ gì.

0

Cú pháp giống như mảng đang được chuyển thành một chuỗi các cuộc gọi Add().

Để xem điều này trong một ví dụ thú vị hơn, hãy xem xét mã sau trong đó tôi thực hiện hai điều thú vị đầu tiên bất hợp pháp trong C#, 1) thiết lập thuộc tính chỉ đọc, 2) thiết lập danh sách với một mảng như 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"} 
}; 

Mã này sẽ hoạt động hoàn hảo, mặc dù 1) MyList chỉ đọc và 2) Tôi đặt danh sách với trình khởi tạo mảng. Lý do tại sao điều này làm việc, là bởi vì trong mã là một phần của trình mã hóa đối tượng, trình biên dịch luôn biến bất kỳ cú pháp nào giống như một cú pháp Add() ngay cả trên một trường chỉ đọc.

Các vấn đề liên quan