2012-02-07 18 views
23

Từ những gì tôi đã đọc, một quyết định thiết kế đã được thực hiện cho một số loại Bộ sưu tập của Bộ sưu tập thành các cấu trúc có thể thay đổi thay vì các loại tham chiếu vì lý do hiệu suất. List.Enumerator là nổi tiếng nhất.Tại sao C# Mảng sử dụng kiểu tham chiếu cho Liệt kê, nhưng Danh sách <T> sử dụng cấu trúc có thể thay đổi?

Tôi đã điều tra một số mã cũ đã sử dụng mảng và ngạc nhiên khi phát hiện ra rằng C# Mảng trả về kiểu SZGenericArrayEnumerator làm kiểu liệt kê chung, là kiểu tham chiếu.

Tôi tự hỏi liệu có ai biết tại sao bộ lặp chung của Array được triển khai như một kiểu tham chiếu hay không khi nhiều bộ sưu tập quan trọng khác sử dụng các cấu trúc có thể thay thế được.

Trả lời

36

Từ những gì tôi đã đọc, quyết định thiết kế đã được thực hiện cho một số loại Bộ sưu tập nhất định là cấu trúc có thể thay đổi thay vì loại tham chiếu vì lý do hiệu suất.

Câu hỏi hay.

Trước hết, bạn đã chính xác. Mặc dù nói chung, các loại giá trị có thể thay đổi là một mã có mùi xấu, trong trường hợp này chúng được hợp lý:

  • Biến đổi gần như hoàn toàn bị ẩn khỏi người dùng.
  • Rất khó có ai sẽ sử dụng điều tra theo cách khó hiểu.
  • Việc sử dụng loại giá trị có thể thay đổi thực sự giải quyết được vấn đề hiệu năng thực tế trong trường hợp cực kỳ phổ biến.

tôi tự hỏi nếu có ai biết lý do tại sao iterator generic Array Avatar của đã được thực hiện như một loại tài liệu tham khảo khi rất nhiều bộ sưu tập quan trọng hiệu suất khác sử dụng cấu trúc có thể thay đổi để thay thế.

Vì nếu bạn là loại người quan tâm đến hiệu suất liệt kê một mảng thì tại sao bạn sử dụng điều tra viên ở nơi đầu tiên? Đó là một mảng vì lợi ích của thiên đàng; chỉ cần viết một vòng lặp for lặp lại các chỉ báo của nó như một người bình thường và không bao giờ phân bổ điều tra viên. (Hoặc một foreach loop; # trình biên dịch C sẽ viết lại foreach loop vào for vòng lặp tương đương nếu nó biết rằng bộ sưu tập vòng lặp là một mảng.)

Lý do duy nhất lý do tại sao bạn muốn có được một Enumerator từ một mảng trong vị trí đầu tiên là nếu bạn đang chuyển nó đến một phương thức có một số IEnumerator<T>, trong trường hợp này là nếu điều tra viên là một cấu trúc thì bạn sẽ được boxing nó. Tại sao phải chịu chi phí làm cho loại giá trị và sau đó đấm bốc nó? Chỉ cần biến nó trở thành kiểu tham chiếu để bắt đầu.

+0

Với sự phổ biến (và sức mạnh biểu cảm) của LINQ, các điều tra viên có khả năng được truy xuất cho các mảng khá thường xuyên. Ví dụ, các lớp phản chiếu trong các mảng trả về .NET cho nhiều thuộc tính/phương thức - và sử dụng LINQ w/reflection khá tiện dụng. Theo hiểu biết của tôi, hầu hết các toán tử LINQ không thực hiện các kiểm tra trường hợp đặc biệt để xác định xem IEnumerable <> chúng đang xử lý thực sự là một mảng hay không. – LBushkin

+0

@LBushkin: Đúng. Nếu bạn không muốn nhận chi phí phân bổ heap và sau đó thu gom rác * enumerator * thì bạn cũng có lẽ không muốn chi phí phân bổ đống và thu gom rác * truy vấn *, và tất cả các đối tượng liệt kê nó tạo ra và tất cả các đại biểu bạn phải tạo. Không phải đề cập đến tất cả các chi phí của việc gọi truy vấn đó (kiểm tra đối số cho là chính xác, và như vậy). LINQ cho các đối tượng là biểu diễn hợp lý nhưng chắc chắn nó không được thiết kế để giảm thiểu phân bổ đống! LINQ phân bổ bộ nhớ heap khắp nơi. –

+0

Một yếu tố khác, tôi nghi ngờ, đó là điều tra cho bản gốc 'Danh sách' được thiết kế trong những ngày trước khi Generics. Cấu trúc-Ness của 'List.Enumerator' chỉ ảnh hưởng đến ba loại mã: mã sử dụng rõ ràng kiểu' List.Enumerator', mã chấp nhận tham số chung bị ràng buộc với 'IEnumerable', hoặc mã sử dụng' var' để khai báo biến sẽ giữ bảng liệt kê của một danh sách. Nếu một lập trình viên sẽ chỉ định 'List.Enumerator', lập trình viên nên biết những gì anh ta đang làm. Đối với hai loại mã khác, chúng không thể tồn tại khi 'List.Enumerator' được thiết kế. – supercat

2

Mảng nhận được một số điều trị đặc biệt trong trình biên dịch C#. Khi bạn sử dụng foreach trên chúng, trình biên dịch dịch nó thành một vòng lặp for. Vì vậy, không có lợi ích hiệu suất trong việc sử dụng struct điều tra viên.

List<T> mặt khác là một lớp đơn giản mà không cần xử lý đặc biệt, do đó, sử dụng cấu trúc sẽ mang lại hiệu suất tốt hơn.

+0

không phải tất cả foreach được biên dịch thành vòng lặp? –

+1

@OskarKjellin không, các vòng lặp 'foreach' bình thường trở thành một cái gì đó giống như' while (enumerator.MoveNext()) {var element = enumerator.Current; ...} 'trong khi' foreach' lặp qua chỉ mục mảng vào mảng ('for (int i = 0; i CodesInChaos

+6

@OskarKjellin: Tất cả các vòng lặp' foreach' và 'for' đều * cuối cùng được biên dịch thành các vòng lặp' while' của CodeInChaos là các vòng lặp foreach' trên mảng là đầu tiên được biên dịch thành vòng lặp kiểu «for (int i = 0; i

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