2012-03-13 43 views
27

Tại sao dòng cuối cùng không được phép?Tại sao IEnumerable <struct> không thể xem như IEnumerable <object>?

IEnumerable<double> doubleenumerable = new List<double> { 1, 2 }; 
IEnumerable<string> stringenumerable = new List<string> { "a", "b" }; 
IEnumerable<object> objects1 = stringenumerable; // OK 
IEnumerable<object> objects2 = doubleenumerable; // Not allowed 

Đây có phải vì double là loại giá trị không lấy được từ đối tượng, do đó hiệp phương sai không hoạt động?

Điều đó có nghĩa rằng không có cách nào để làm cho công việc này:

public interface IMyInterface<out T> 
{ 
    string Method(); 
} 

public class MyClass<U> : IMyInterface<U> 
{ 
    public string Method() 
    { 
     return "test"; 
    } 
} 

public class Test 
{ 
    public static object test2() 
    { 
     IMyInterface<double> a = new MyClass<double>(); 
     IMyInterface<object> b = a; // Invalid cast! 
     return b.Method(); 
    } 
} 

Và đó tôi cần phải viết của tôi rất riêng IMyInterface<T>.Cast<U>() để làm điều đó?

+0

bản sao có thể có của [Tại sao hiệp phương sai và đối nghịch không hỗ trợ loại giá trị] (http://stackoverflow.com/questions/12454794/why-covariance-and-contravariance-do-not-support-value-type) – nawfal

Trả lời

44

Tại sao dòng cuối cùng không được phép?

Vì đôi là loại giá trị và đối tượng là loại tham chiếu; hiệp phương sai chỉ hoạt động khi cả hai loại là loại tham chiếu.

Đây có phải vì double là loại giá trị không lấy được từ đối tượng, do đó hiệp phương sai không hoạt động?

No. Số đôi không xuất phát từ đối tượng. Tất cả các loại giá trị xuất phát từ đối tượng.

Bây giờ câu hỏi bạn nên hỏi:

Tại sao hiệp phương sai không hoạt động để chuyển đổi IEnumerable<double> để IEnumerable<object>?

ai làm boxing? Một chuyển đổi từ đôi thành đối tượng phải hộp số tăng gấp đôi. Giả sử bạn có cuộc gọi đến IEnumerator<object>.Current đó là "thực sự" cuộc gọi đến triển khai IEnumerator<double>.Current. Người gọi mong đợi một đối tượng được trả về. Callee trả về gấp đôi. Mã lệnh thực hiện lệnh đấm bốc ở đâu mà biến số double được trả về bởi IEnumerator<double>.Current thành một hộp tăng gấp đôi?

Đó là không nơi nào, đó là nơi và đó là lý do tại sao chuyển đổi này là bất hợp pháp. Cuộc gọi đến Current sẽ đặt một số tám byte lên ngăn xếp đánh giá, và người tiêu dùng sẽ mong đợi một tham chiếu bốn byte đến một hộp kép trên ngăn xếp đánh giá, và vì vậy người tiêu dùng sẽ sụp đổ và chết khủng khiếp với ngăn xếp không đúng và tham chiếu đến bộ nhớ không hợp lệ.

Nếu bạn muốn mã mà hộp để thực hiện sau đó nó phải được viết tại một số điểm, và bạn là người được để viết nó. Cách đơn giản nhất là sử dụng phương pháp Cast<T> mở rộng:

IEnumerable<object> objects2 = doubleenumerable.Cast<object>(); 

Bây giờ bạn gọi một phương thức helper có chứa các hướng dẫn đấm bốc có thể chuyển đổi các đôi từ tám byte kép để tham khảo.

CẬP NHẬT: Người nhận xét lưu ý rằng tôi đã trả lời câu hỏi bằng cách giả định sự tồn tại của cơ chế giải quyết vấn đề một cách khó khăn như giải pháp cho câu hỏi ban đầu. Làm thế nào để thực hiện Cast<T> quản lý để giải quyết vấn đề biết có nên đóng hộp hay không?

Nó hoạt động như bản phác thảo này. Lưu ý rằng các loại tham số là không generic:

public static IEnumerable<T> Cast<T>(this IEnumerable sequence) 
{ 
    if (sequence == null) throw ... 
    if (sequence is IEnumerable<T>) 
     return sequence as IEnumerable<T>; 
    return ReallyCast<T>(sequence); 
} 

private static IEnumerable<T> ReallyCast<T>(IEnumerable sequence) 
{ 
    foreach(object item in sequence) 
     yield return (T)item; 
} 

Trách nhiệm để xác định liệu các diễn viên từ đối tượng để T là một chuyển đổi unboxing hoặc chuyển đổi tài liệu tham khảo được hoãn lại để thời gian chạy. Jitter biết liệu T là kiểu tham chiếu hay kiểu giá trị. 99% thời gian, tất nhiên nó sẽ là một kiểu tham chiếu.

+6

Tắt chủ đề, nhưng tôi muốn bạn tweet. – Joe

+1

1+ Như thường lệ nhanh hơn khi đăng. Tôi thích "vì vậy người tiêu dùng sẽ sụp đổ và chết khủng khiếp với một ngăn xếp lệch và một tham chiếu đến bộ nhớ không hợp lệ" một phần. –

+17

@JoeTuskan: Khi tôi có điều gì đó để nói rằng phù hợp với 120 ký tự và là mối quan tâm chung, tôi sẽ. Mong chờ đợi lâu. –

4

Để hiểu những gì được phép và không được phép, và tại sao mọi thứ hoạt động như họ làm, nó là hữu ích để hiểu những gì đang xảy ra dưới mui xe. Đối với mọi loại giá trị, có tồn tại một loại đối tượng lớp tương ứng, giống như tất cả các đối tượng - sẽ kế thừa từ System.Object. Mỗi đối tượng lớp bao gồm dữ liệu của nó với từ dài 32 bit (x86) hoặc từ dài 64 bit (x64) xác định loại của nó. Tuy nhiên, các vị trí lưu trữ kiểu giá trị không chứa các đối tượng hoặc tham chiếu lớp như vậy, cũng như chúng không có một từ dữ liệu kiểu được lưu trữ với chúng. Thay vào đó, mỗi vị trí kiểu giá trị nguyên thủy chỉ giữ các bit cần thiết để biểu diễn một giá trị, và mỗi vị trí lưu trữ kiểu giá trị cấu trúc chỉ đơn giản là giữ nội dung của tất cả các trường công khai và riêng tư thuộc loại đó.

Khi một bản sao một biến loại Double thành một loại Object, người ta tạo một thể hiện mới của loại đối tượng lớp được liên kết với Double và sao chép tất cả byte từ gốc sang đối tượng lớp mới. Mặc dù kiểu đóng gói-Double có cùng tên với loại giá trị Double, điều này không dẫn đến sự mơ hồ vì chúng thường không được sử dụng trong cùng một ngữ cảnh. Vị trí lưu trữ của các loại giá trị giữ bit thô hoặc kết hợp các trường, không có thông tin loại được lưu trữ; sao chép một vị trí lưu trữ này sang một vị trí khác sao chép tất cả các byte và do đó sao chép tất cả các trường công khai và riêng tư. Ngược lại, các đối tượng heap của các kiểu có nguồn gốc từ các kiểu giá trị là các đối tượng đống, và hành xử giống như các đối tượng đống. Mặc dù C# coi các nội dung của các vị trí lưu trữ kiểu giá trị như thể chúng là các dẫn xuất của Object, bên dưới nội dung của các vị trí lưu trữ đó đơn giản là các bộ sưu tập byte, có hiệu quả bên ngoài hệ thống kiểu. Vì chúng chỉ có thể được truy cập bằng mã mà biết những gì các byte đại diện, không cần phải lưu trữ thông tin đó với chính địa chỉ lưu trữ. Mặc dù sự cần thiết cho quyền anh khi gọi số GetType trên cấu trúc thường được mô tả dưới dạng GetType là một chức năng không phải là bóng mờ, không thực tế ảo, thực tế là nội dung của vị trí lưu trữ loại giá trị (khác biệt với bản thân vị trí) không có thông tin về loại.

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