2010-06-05 14 views
7

tôi this MSDN Magazine article, các tiểu bang tác giả (tôi nhấn mạnh):Tính năng unboxing chỉ trả về một con trỏ tới giá trị trong đối tượng được đóng hộp trên heap?

Lưu ý rằng đấm bốc luôn tạo ra một đối tượng mới và các bản sao bit giá trị không có hộp bọc của đối tượng. Mặt khác, unboxing chỉ cần trả về con trỏ tới dữ liệu trong một đối tượng được đóng hộp: không xuất hiện bản sao bộ nhớ. Tuy nhiên, đó là thường là trường hợp mã của bạn sẽ làm cho dữ liệu được chỉ dẫn bởi tham chiếu không được lấy hộp thư sẽ được sao chép.

Tôi bị nhầm lẫn bởi câu tôi đã in đậm và câu sau đó. Từ mọi thứ khác tôi đã đọc, bao gồm this MSDN page, tôi chưa bao giờ nghe nói rằng unboxing chỉ trả về một con trỏ tới giá trị trên heap. Tôi đã được ấn tượng rằng unboxing sẽ dẫn đến việc bạn có một biến chứa một bản sao của giá trị trên stack, giống như bạn bắt đầu. Sau khi tất cả, nếu biến của tôi có chứa "một con trỏ đến giá trị trên heap", sau đó tôi đã không có một loại giá trị, tôi đã có một con trỏ.

Ai đó có thể giải thích điều này có nghĩa là gì? Là tác giả trên crack? (Có ít nhất một lỗi khác rõ ràng trong bài viết). Và nếu điều này là đúng, các trường hợp là "mã của bạn sẽ làm cho dữ liệu được trỏ đến bởi tham chiếu không được đóng hộp nào được sao chép"?

Tôi chỉ nhận thấy rằng bài viết đã gần 10 tuổi, vì vậy có lẽ đây là thứ đã thay đổi rất sớm trong cuộc đời của .Net.

Trả lời

6

Bài viết là chính xác. Tuy nhiên nó nói về những gì thực sự đi vào, không phải những gì IL trông giống như trình biên dịch tạo ra. Sau khi tất cả, một chương trình .NET không bao giờ thực hiện IL, nó thực hiện mã máy được tạo ra từ IL bởi trình biên dịch JIT.

Và opcode unbox thực sự tạo mã tạo con trỏ tới các bit trên heap đại diện cho giá trị loại giá trị. JIT tạo ra một cuộc gọi đến một hàm trợ giúp nhỏ trong CLR có tên là "JIT_Unbox". clr \ src \ vm \ jithelpers.cpp nếu bạn nhận được mã nguồn SSCLI20. Hàm Object :: GetData() trả về con trỏ.

Từ đó, giá trị phổ biến nhất đầu tiên được sao chép vào thanh ghi CPU. Mà sau đó có thể được lưu trữ ở đâu đó. Nó không phải là ngăn xếp, nó có thể là một thành viên của một đối tượng kiểu tham chiếu (gc heap). Hoặc một biến tĩnh (đống bộ nạp). Hoặc nó có thể được đẩy vào ngăn xếp (gọi phương thức). Hoặc thanh ghi CPU có thể được sử dụng như là khi giá trị được sử dụng trong một biểu thức.

Trong khi gỡ lỗi, nhấp chuột phải vào cửa sổ trình chỉnh sửa và chọn "Chuyển sang tháo gỡ" để xem mã máy.

+0

Tôi hiểu! Cảm ơn bạn. –

1

Đấm bốc là hành động tạo một thể hiện kiểu giá trị cho một thể hiện kiểu tham chiếu (hoặc là object hoặc một giao diện) và các kiểu tham chiếu được cấp phát trên heap.

Theo 'C# 4.0 in a Nutshell': "... unboxing sao chép nội dung của đối tượng trở lại thành thể hiện loại giá trị" và điều đó ngụ ý trên ngăn xếp.

Trong bài viết bạn tham khảo, các tiểu bang tác giả:

public static void Main() { 

    Int32 v = 5; // Create an unboxed value type variable 
    Object o = v; // o refers to a boxed version of v 
    v = 123;  // Changes the unboxed value to 123 

    Console.WriteLine(v + ", " + (Int32) o); // Displays "123, 5" 
} 

Từ mã này, bạn có thể đoán có bao nhiêu hoạt động đấm bốc xảy ra? Bạn có thể là ngạc nhiên khi phát hiện ra rằng câu trả lời là ba! Hãy phân tích mã số một cách cẩn thận để thực sự hiểu những gì đang xảy ra . Đầu tiên, loại giá trị không được phân loại Int32 (v) được tạo và được khởi tạo thành 5. Sau đó, kiểu tham chiếu đối tượng (o) được tạo và nó muốn trỏ đến v. Nhưng các loại tham chiếu phải luôn là cho các đối tượng trong vùng , do đó, C# đã tạo mã IL thích hợp cho hộp v và lưu trữ địa chỉ của phiên bản đóng hộp của v trong o. Bây giờ 123 là unboxed và dữ liệu được tham chiếu được sao chép vào loại giá trị không được mã hóa v; điều này không có hiệu lực trên phiên bản đóng hộp của v, vì vậy phiên bản đóng hộp giữ giá trị của nó là 5. Lưu ý rằng ví dụ này cho thấy o được unboxed (trả về con trỏ tới dữ liệu trong o), và sau đó dữ liệu trong o là bộ nhớ được sao chép sang giá trị không được mở hộp loại v.

+0

Có, mọi thứ tôi đã đọc nói hoặc ngụ ý rằng dữ liệu được sao chép trở lại ngăn xếp. Đối với lời giải thích này mâu thuẫn với chính nó: có, và một phần của trích dẫn bạn dán đậm là thực sự những gì tôi đã đề cập đến khi tôi nói có một lỗi trong bài báo. Đó là hoàn toàn sai - o không được unboxed vào v bất cứ nơi nào trong ví dụ. –

5

Tác giả của bài viết gốc phải đề cập đến những gì đang diễn ra ở cấp độ IL. Có hai mã mở hộp giải mã: unboxunbox.any.

Theo MSDN, regarding unbox.any:

Khi áp dụng cho các hình thức đóng hộp của một loại giá trị , hướng dẫn unbox.any trích xuất các giá trị chứa trong obj (loại O), và do đó là tương đương với unbox theo sau là ldobj.

regarding unbox:

[...] Unbox không cần phải sao chép kiểu giá trị từ đối tượng. Thông thường, nó chỉ cần tính địa chỉ của loại giá trị là đã có mặt bên trong đối tượng đóng hộp .

Vì vậy, tác giả biết điều ông đang nói đến.

Thực tế nhỏ này về unbox giúp bạn có thể thực hiện một số tối ưu hóa tiện lợi khi làm việc trực tiếp với IL. Ví dụ, nếu bạn có một hộp int mà bạn cần phải chuyển đến một hàm chấp nhận một int ref, bạn chỉ có thể phát ra một opcode unbox và tham chiếu đến int sẽ sẵn sàng trong ngăn xếp cho hàm hoạt động. Trong trường hợp này, hàm sẽ thay đổi nội dung thực tế của đối tượng boxing, một thứ không thể thực hiện được ở cấp C#. Nó giúp bạn tiết kiệm từ việc cần phân bổ không gian cho một biến cục bộ tạm thời, unbox int trong đó, chuyển một ref tới int đến hàm, và sau đó tạo một đối tượng boxing mới để re-box int, loại bỏ hộp cũ. Tất nhiên, khi bạn đang làm việc ở cấp độ C#, bạn không thể thực hiện bất kỳ sự tối ưu nào như vậy, vì vậy điều thường xảy ra là mã được trình biên dịch tạo ra sẽ hầu như luôn sao chép biến từ đối tượng đã đóng trước khi sử dụng thêm nữa.

+0

+1 Ngoài ra tuyệt vời, cảm ơn bạn. –