Thật vậy, đó là vì lý do hiệu suất. Nhóm BCL đã thực hiện lô nghiên cứu về điểm này trước khi quyết định đi theo những gì bạn gọi đúng là thực tiễn đáng ngờ và nguy hiểm: việc sử dụng loại giá trị có thể thay đổi.
Bạn hỏi tại sao điều này không gây ra quyền anh. Đó là bởi vì trình biên dịch C# không tạo mã cho các công cụ hộp để IEnumerable hoặc IEnumerator trong một vòng lặp foreach nếu nó có thể tránh nó!
Khi chúng ta thấy
foreach(X x in c)
điều đầu tiên chúng tôi làm là kiểm tra xem nếu c có một phương pháp gọi là GetEnumerator. Nếu có, thì chúng ta kiểm tra xem kiểu nó trả về có phương thức MoveNext và thuộc tính hiện tại không. Nếu có, thì vòng lặp foreach được tạo hoàn toàn bằng cách sử dụng các cuộc gọi trực tiếp đến các phương thức và thuộc tính đó. Chỉ khi "mẫu" không thể khớp với nhau thì chúng ta sẽ quay trở lại để tìm kiếm các giao diện.
Điều này có hai hiệu ứng mong muốn.
Đầu tiên, nếu bộ sưu tập là một tập hợp các int, nhưng được viết trước khi loại chung được phát minh, thì nó không lấy hình phạt quyền anh của giá trị Hiện tại thành đối tượng và sau đó unboxing nó thành int. Nếu Current là thuộc tính trả về int, chúng ta chỉ sử dụng nó.
Thứ hai, nếu điều tra viên là loại giá trị thì nó không được liệt kê trong bảng điều tra cho IEnumerator. Như tôi đã nói, nhóm BCL đã làm rất nhiều nghiên cứu về điều này và phát hiện ra rằng phần lớn thời gian, hình phạt phân bổ và deallocating điều tra đủ lớn mà nó đã được giá trị làm cho nó một loại giá trị , mặc dù làm như vậy có thể gây ra một số lỗi điên rồ.
Ví dụ, hãy xem xét điều này:
struct MyHandle : IDisposable { ... }
...
using (MyHandle h = whatever)
{
h = somethingElse;
}
Bạn sẽ khá đúng mong đợi những nỗ lực đột biến h thất bại, và thực sự nó làm. Trình biên dịch phát hiện rằng bạn đang cố gắng thay đổi giá trị của một cái gì đó có một xử lý đang chờ xử lý, và làm như vậy có thể gây ra các đối tượng cần phải được xử lý để thực sự không được xử lý.
Bây giờ giả sử bạn có:
struct MyHandle : IDisposable { ... }
...
using (MyHandle h = whatever)
{
h.Mutate();
}
gì xảy ra ở đây? Bạn có thể mong đợi hợp lý rằng trình biên dịch sẽ làm những gì nó làm nếu h là một trường chỉ đọc: make a copy, and mutate the copy để đảm bảo rằng phương thức không vứt bỏ các thứ trong giá trị cần phải được xử lý.
Tuy nhiên, đó mâu thuẫn với trực giác của chúng ta về những gì phải xảy ra ở đây:
using (Enumerator enumtor = whatever)
{
...
enumtor.MoveNext();
...
}
Chúng tôi hy vọng rằng làm một MoveNext bên trong một sử dụng khối sẽ di chuyển Enumerator kế tiếp bất kể đó là một cấu trúc hoặc một kiểu ref.
Thật không may, trình biên dịch C# ngày nay có lỗi. Nếu bạn đang ở trong tình huống này, chúng tôi chọn chiến lược nào để theo dõi không nhất quán. Các hành vi hôm nay là:
nếu con người biến giá trị gia gõ đột biến thông qua một phương pháp là một bình thường địa phương sau đó nó bị đột biến thường
nhưng nếu nó là một địa phương kéo lên (vì đó là một closed- trên biến của một hàm ẩn danh hoặc trong khối lặp) thì địa chỉ là thực sự được tạo dưới dạng trường chỉ đọc và thiết bị đảm bảo các đột biến xảy ra trên bản sao.
Rất tiếc, thông số cung cấp ít hướng dẫn về vấn đề này. Rõ ràng một cái gì đó bị hỏng bởi vì chúng tôi đang làm nó không nhất quán, nhưng những gì quyền điều cần làm là không rõ ràng.
trang liên quan cho những người khác chạy trên này: http://stackoverflow.com/questions/384511/enumerator-implementation-use-struct-or-class http://www.eggheadcafe.com/software/ aspnet/31702392/c-compiler-challenge - s.aspx –