2009-01-19 29 views
55

tôi đang tìm kiếm tại initializers # sưu tập C và thấy việc thực hiện là rất thực dụng nhưng cũng rất không giống như bất cứ điều gì khác trong C#Tại sao bộ khởi tạo bộ sưu tập C# hoạt động theo cách này?

tôi có thể tạo mã như thế này:

using System; 
using System.Collections; 

class Program 
{ 
    static void Main() 
    { 
     Test test = new Test { 1, 2, 3 }; 
    } 
} 

class Test : IEnumerable 
{ 
    public IEnumerator GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 

    public void Add(int i) { } 
} 

Kể từ khi tôi đã đáp ứng được yêu cầu tối thiểu cho trình biên dịch (thực hiện IEnumerablepublic void Add) điều này hoạt động nhưng rõ ràng là không có giá trị.

Tôi đã tự hỏi điều gì đã ngăn nhóm C# tạo ra một bộ yêu cầu nghiêm ngặt hơn? Nói cách khác, tại sao, để cú pháp này biên dịch, trình biên dịch có yêu cầu loại thực thi ICollection không? Điều đó có vẻ nhiều hơn trong tinh thần của các tính năng C# khác.

+3

Chỉ cần một lưu ý cho nhà phát triển web: rằng NotImplementedException sẽ bị ném nếu bạn cố gắng tuần tự hóa một cá thể của lớp đó thành JSON (thực thi IEnumerable làm cho trình nối tiếp lặp qua đối tượng) – diachedelic

Trả lời

87

Quan sát của bạn là ngay tại chỗ - trên thực tế, nó phản ánh một được thực hiện bởi Mads Torgersen, một Microsoft C# Language PM.

Mads làm một bài đăng trong tháng 10 năm 2006 về chủ đề này với tựa đề What Is a Collection? trong đó ông đã viết:

thừa nhận, chúng ta thổi nó trong phiên bản đầu tiên của khuôn khổ này với System.Collections.ICollection, trong đó là bên cạnh vô dụng. Nhưng chúng tôi cố định nó lên khá tốt khi Generics đến cùng trong .NET framework 2.0: System.Collections.Generic.ICollection < T> cho phép bạn Add và Remove yếu tố, liệt kê chúng, Đếm họ và kiểm tra cho thành viên.

Rõ ràng từ đó trở đi, mọi người sẽ triển khai ICollection <T> mỗi lần họ tạo bộ sưu tập, phải không? Không phải vậy. Đây là cách chúng tôi đã sử dụng LINQ để tìm hiểu về bộ sưu tập thực sự là gì và cách chúng tôi đã thay đổi ngôn ngữ của chúng tôi thiết kế trong C# 3.0.

Nó chỉ ra rằng chỉ có 14 triển khai của ICollection<T> trong khuôn khổ, nhưng 189 lớp mà thực hiện IEnumerable và có một Add() phương pháp công cộng.

Có một lợi ích ẩn đối với phương pháp này - nếu chúng đã dựa trên giao diện ICollection<T>, sẽ có chính xác một phương thức được hỗ trợ Add().

Ngược lại, cách tiếp cận mà họ đã thực hiện có nghĩa là các trình khởi tạo cho bộ sưu tập chỉ là các bộ đối số cho các phương thức Add().

Để minh họa, chúng ta hãy mở rộng mã của bạn một chút:

class Test : IEnumerable 
{ 
    public IEnumerator GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 

    public void Add(int i) { } 

    public void Add(int i, string s) { } 
} 

Bạn có thể bây giờ viết này:

class Program 
{ 
    static void Main() 
    { 
     Test test 
      = new Test 
      { 
       1, 
       { 2, "two" }, 
       3 
      }; 
    } 
} 
+0

Trong báo giá, dòng thứ 2 dòng 1, đó là "ICollection " không phải "ICollection". –

+0

Bạn nói đúng - tôi đã quên lãng mã hóa các ký tự ít hơn và lớn hơn. Đã sửa. – Bevan

+0

Câu trả lời của bạn minh họa những lợi ích/tính linh hoạt mà nó cung cấp, nhưng tôi phải tự hỏi liệu điều này có nghĩa là không có cách hiệu quả để cung cấp sự linh hoạt như vậy trong các tình huống tương tự. Họ đã viết một cách hiệu quả vào "spec" nếu bạn có một phương thức 'Add' ở đâu đó trong một hệ thống phân cấp đang thực hiện' IEnumerable', sau đó chúng ta sẽ thực hiện một số hokus-pokus trong trình biên dịch và khởi tạo nó, mặc dù phương thức không phải là tình trạng quá tải, thành viên giao diện hoặc theo bất kỳ cách nào được chỉ định cho mục đích như vậy thông qua cú pháp, thuộc tính hoặc các phương tiện khác. " – AaronLS

9

Tôi nghĩ về điều này quá, và câu trả lời mà đáp ứng cho tôi nhất là ICollection có nhiều phương thức khác với Thêm, chẳng hạn như: Xóa, Chứa, Sao chép và Xoá.Loại bỏ các yếu tố hoặc thanh toán bù trừ không có gì để làm với việc có thể hỗ trợ cú pháp khởi tạo đối tượng, tất cả những gì bạn cần là một Add().

Nếu khung được thiết kế đủ chi tiết và có giao diện ICollectionAdd, thì nó sẽ có thiết kế "hoàn hảo". Nhưng tôi thành thật không nghĩ rằng sẽ có thêm nhiều giá trị, có một phương pháp cho mỗi giao diện. IEnumerable + Thêm có vẻ giống như một cách tiếp cận hackish, nhưng khi bạn nghĩ về nó, nó là một lựa chọn tốt hơn.

EDIT: Đây không phải là lần duy nhất C# tiếp cận sự cố với loại giải pháp này. Kể từ .NET 1.1, foreach sử dụng kiểu gõ vịt để liệt kê một bộ sưu tập, tất cả các lớp của bạn cần thực hiện là GetEnumerator, MoveNext và Current. Kirill Osenkov cũng có một số post hỏi câu hỏi của bạn.

+0

Đó là "nguyên tắc phân biệt giao diện". –

3

(Tôi biết tôi là 3 năm cuối về điều này, nhưng tôi đã không hài lòng với câu trả lời đã có.)

lý do tại sao, để cho cú pháp này để biên dịch, không trình biên dịch không đòi hỏi rằng loại thực hiện ICollection?

Tôi sẽ đảo ngược câu hỏi của bạn: Điều gì sẽ xảy ra nếu trình biên dịch có yêu cầu không thực sự cần thiết?

Non- ICollection các lớp cũng có thể hưởng lợi từ cú pháp trình khởi tạo bộ sưu tập. Xem xét các lớp cho phép thêm dữ liệu vào chúng mà không cho phép truy cập vào dữ liệu đã thêm trước đó.

Cá nhân, tôi thích sử dụng cú pháp new Data { { ..., ... }, ... } để thêm giao diện giống như DSL, ánh sáng vào mã kiểm tra đơn vị của tôi.

Thực ra, tôi thà làm suy yếu yêu cầu, để tôi có thể sử dụng cú pháp đẹp mà không cần phải bận tâm triển khai IEnumerable. Bộ khởi tạo bộ sưu tập là đường cú pháp thuần túy cho Add(), chúng không yêu cầu gì khác.

+4

Tôi thấy quan điểm của bạn, nhưng nếu cú ​​pháp cú pháp đòi hỏi một phương pháp, tôi muốn tìm hiểu về điều đó thông qua một giao diện. –

+0

@ nik.shornikov Giống như ICollectionInitializerSyntax (và tất cả các biến thể )? Tôi đồng ý, điều này sẽ làm cho ý định của mã rõ ràng hơn. Và nó sẽ đi độc đáo với mong muốn của tôi làm suy yếu yêu cầu IEnumerable hiện tại. –

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