Quyền anh và unboxing là gì và sự đánh đổi là gì?


135

Tôi đang tìm kiếm một câu trả lời rõ ràng, súc tích và chính xác.

Lý tưởng như câu trả lời thực tế, mặc dù liên kết đến lời giải thích tốt chào đón.


2
Đây thực sự là ngôn ngữ bất khả tri?
Henk Holterman

3
@HenkHolterman chắc chắn không phải là ngôn ngữ cụ thể, mặc dù nó cũng không liên quan đến tất cả các ngôn ngữ - ví dụ, sự khác biệt sẽ không liên quan đến hầu hết các ngôn ngữ được gõ động. Tôi không chắc thẻ nào có thể được sử dụng thay thế - language-but-not-type-agnostic? static-language-agnostic? Tôi không chắc chắn rằng SO cần sự khác biệt; có thể là một câu hỏi hay cho meta
Keith

Câu trả lời:


189

Các giá trị được đóng hộp là các cấu trúc dữ liệu là các hàm bao tối thiểu xung quanh các kiểu nguyên thủy *. Các giá trị được đóng hộp thường được lưu trữ dưới dạng con trỏ tới các đối tượng trên heap .

Do đó, các giá trị được đóng hộp sử dụng nhiều bộ nhớ hơn và mất tối thiểu hai lần tra cứu bộ nhớ để truy cập: một lần để lấy con trỏ và một lần khác để theo con trỏ đó đến nguyên thủy. Rõ ràng đây không phải là điều bạn muốn trong các vòng lặp bên trong của bạn. Mặt khác, các giá trị được đóng hộp thường chơi tốt hơn với các loại khác trong hệ thống. Vì chúng là cấu trúc dữ liệu hạng nhất trong ngôn ngữ, chúng có siêu dữ liệu và cấu trúc dự kiến ​​mà các cấu trúc dữ liệu khác có.

Trong các bộ sưu tập chung Java và Haskell không thể chứa các giá trị chưa được đóng hộp. Các bộ sưu tập chung trong .NET có thể giữ các giá trị không có hộp mà không bị phạt. Trong đó các tổng quát của Java chỉ được sử dụng để kiểm tra kiểu thời gian biên dịch, .NET sẽ tạo các lớp cụ thể cho từng loại chung được khởi tạo trong thời gian chạy .

Java và Haskell có các mảng không được đóng hộp, nhưng chúng không thuận tiện hơn các bộ sưu tập khác. Tuy nhiên, khi cần hiệu suất cao nhất, nó có giá trị một chút bất tiện để tránh sự quá tải của quyền anh và unboxing.

* Đối với cuộc thảo luận này, một giá trị nguyên thủy là bất kỳ giá trị nào có thể được lưu trữ trên ngăn xếp cuộc gọi , thay vì được lưu trữ dưới dạng con trỏ tới một giá trị trên heap. Thường thì đó chỉ là các loại máy (ints, float, v.v.), cấu trúc và đôi khi là các mảng có kích thước tĩnh. .NET-Land gọi chúng là các loại giá trị (trái ngược với các loại tham chiếu). Người Java gọi chúng là các kiểu nguyên thủy. Haskellions chỉ cần gọi chúng là hộp thư đến.

** Tôi cũng tập trung vào Java, Haskell và C # trong câu trả lời này, vì đó là những gì tôi biết. Đối với những gì đáng giá, Python, Ruby và Javascript đều có các giá trị được đóng hộp riêng. Điều này còn được gọi là phương pháp "Mọi thứ là một đối tượng" ***.

*** Hãy cẩn thận: Trong một số trường hợp, một trình biên dịch / JIT đủ nâng cao có thể thực sự phát hiện ra rằng một giá trị được đóng hộp về mặt ngữ nghĩa khi nhìn vào nguồn, có thể là một giá trị không được lưu trong hộp khi chạy. Về bản chất, nhờ những người triển khai ngôn ngữ xuất sắc, hộp của bạn đôi khi được miễn phí.


Tại sao mặc dù giá trị đóng hộp, CLR mang lại lợi ích gì hay bất cứ thứ gì có được giá trị quyền anh?
positiveGuy

Nói tóm lại (ha ha), chúng chỉ là một Vật thể khác, tiện lợi hơn bao giờ hết. Các nguyên thủy (ít nhất là trong Java) không xuất phát từ Object, không thể có các trường, không thể có các phương thức và thường chỉ hoạt động rất khác với các loại giá trị khác. Mặt khác, làm việc với họ có thể rất nhanh và hiệu quả về không gian. Do đó đánh đổi.
Peter Burns

2
Javascript đã được gọi là mảng được gõ (UInt32Array mới, v.v.) là các mảng của ints và float không có hộp.
nponeccop

126

từ C # 3.0 Tóm lại :

Quyền anh là hành động đúc một loại giá trị thành một loại tham chiếu:

int x = 9; 
object o = x; // boxing the int

unboxing là ... ngược lại:

// unboxing o
object o = 9; 
int x = (int)o; 

72

Boxing & unboxing là quá trình chuyển đổi một giá trị nguyên thủy thành một lớp bao bọc hướng đối tượng (đấm bốc) hoặc chuyển đổi một giá trị từ một lớp bao bọc hướng đối tượng trở lại giá trị nguyên thủy (unboxing).

Ví dụ, trong java, bạn có thể cần phải chuyển đổi một intgiá trị thành Integer(quyền anh) nếu bạn muốn lưu trữ nó trong một Collectionvì các nguyên thủy không thể được lưu trữ trong một Collection, chỉ các đối tượng. Nhưng khi bạn muốn lấy lại từ đó, Collectionbạn có thể muốn nhận giá trị dưới dạng intvà không phải Integervì vậy bạn sẽ bỏ hộp.

Quyền anh và unboxing vốn không phải là xấu , nhưng đó là một sự đánh đổi. Tùy thuộc vào việc thực hiện ngôn ngữ, nó có thể chậm hơn và tốn nhiều bộ nhớ hơn là chỉ sử dụng nguyên thủy. Tuy nhiên, nó cũng có thể cho phép bạn sử dụng các cấu trúc dữ liệu cấp cao hơn và đạt được tính linh hoạt cao hơn trong mã của bạn.

Ngày nay, nó thường được thảo luận nhiều nhất trong ngữ cảnh của tính năng "autoboxing / autounboxing" của Java (và các ngôn ngữ khác). Dưới đây là một lời giải thích trung tâm java của autoboxing .


23

Trong lưới:

Thường thì bạn không thể dựa vào loại biến mà một hàm sẽ tiêu thụ, vì vậy bạn cần sử dụng một biến đối tượng kéo dài từ mẫu số chung thấp nhất - trong .Net đây là object.

Tuy nhiên objectlà một lớp và lưu trữ nội dung của nó như một tài liệu tham khảo.

List<int> notBoxed = new List<int> { 1, 2, 3 };
int i = notBoxed[1]; // this is the actual value

List<object> boxed = new List<object> { 1, 2, 3 };
int j = (int) boxed[1]; // this is an object that can be 'unboxed' to an int

Trong khi cả hai đều giữ cùng một thông tin, danh sách thứ hai lớn hơn và chậm hơn. Mỗi giá trị trong danh sách thứ hai thực sự là một tham chiếu đến một giá trị objectgiữ int.

Điều này được gọi là đóng hộp vì intđược bao bọc bởi object. Khi truyền lại, intnó không được đóng hộp - được chuyển đổi trở lại giá trị của nó.

Đối với các loại giá trị (tức là tất cả structs), điều này chậm và có khả năng sử dụng nhiều không gian hơn.

Đối với các loại tham chiếu (tức là tất cả classes), vấn đề này ít hơn nhiều, vì dù sao chúng cũng được lưu trữ dưới dạng tham chiếu.

Một vấn đề nữa với loại giá trị được đóng hộp là không rõ ràng rằng bạn đang xử lý hộp, hơn là giá trị. Khi bạn so sánh hai structsthì bạn đang so sánh các giá trị, nhưng khi bạn so sánh hai classesthì (theo mặc định) bạn đang so sánh tham chiếu - tức là đây có phải là cùng một ví dụ không?

Điều này có thể gây nhầm lẫn khi xử lý các loại giá trị được đóng hộp:

int a = 7;
int b = 7;

if(a == b) // Evaluates to true, because a and b have the same value

object c = (object) 7;
object d = (object) 7;

if(c == d) // Evaluates to false, because c and d are different instances

Thật dễ dàng để làm việc xung quanh:

if(c.Equals(d)) // Evaluates to true because it calls the underlying int's equals

if(((int) c) == ((int) d)) // Evaluates to true once the values are cast

Tuy nhiên, đó là một điều cần cẩn thận khi xử lý các giá trị được đóng hộp.


1
Trong vb.net, sự khác biệt giữa ngữ nghĩa bình đẳng rõ ràng hơn, Objectkhông thực hiện toán tử đẳng thức, nhưng các loại lớp có thể được so sánh với Istoán tử; ngược lại, Int32có thể được sử dụng với toán tử đẳng thức, nhưng không Is. Sự khác biệt đó làm cho loại hình so sánh đang được thực hiện rõ ràng hơn nhiều.
supercat

4

Boxinglà quá trình chuyển đổi một loại giá trị thành một loại tham chiếu. Trong khi đó Unboxinglà việc chuyển đổi loại tham chiếu thành loại giá trị.

EX: int i = 123;
    object o = i;// Boxing
    int j = (int)o;// UnBoxing

Các loại giá trị là : int, charstructures, enumerations. Các loại tài liệu tham khảo là: Classes, interfaces, arrays, stringsobjects


3

Bộ sưu tập chung .NET FCL:

List<T>
Dictionary<TKey, UValue>
SortedDictionary<TKey, UValue>
Stack<T>
Queue<T>
LinkedList<T>

tất cả đều được thiết kế để khắc phục các vấn đề về hiệu suất của quyền anh và unboxing trong các triển khai bộ sưu tập trước đó.

Để biết thêm, xem chương 16, CLR qua C # (Phiên bản 2) .


1

Quyền anh và unboxing tạo điều kiện cho các loại giá trị được coi là đối tượng. Quyền anh có nghĩa là chuyển đổi một giá trị thành một thể hiện của loại tham chiếu đối tượng. Ví dụ, Intlà một lớp và intlà một kiểu dữ liệu. Chuyển đổi intthành Intmột ví dụ về quyền anh, trong khi chuyển đổi Intsang intlà bỏ hộp. Khái niệm này giúp thu gom rác, Unboxing, mặt khác, chuyển đổi loại đối tượng thành loại giá trị.

int i=123;
object o=(object)i; //Boxing

o=123;
i=(int)o; //Unboxing.

Trong javascript, var ii = 123; typeof ii trả về number. var iiObj = new Number(123); typeof iiObjtrả lại object. typeof ii + iiObjtrả lại number. Vì vậy, đây là javascript tương đương với quyền anh. Giá trị iiObj được tự động chuyển đổi thành số nguyên thủy (chưa được đóng hộp) để thực hiện số học và trả về giá trị không có hộp.
PatS

-2

Giống như bất cứ điều gì khác, autoboxing có thể có vấn đề nếu không được sử dụng cẩn thận. Cổ điển là kết thúc với một NullPulumException và không thể theo dõi nó. Ngay cả với một trình sửa lỗi. Thử cái này:

public class TestAutoboxNPE
{
    public static void main(String[] args)
    {
        Integer i = null;

        // .. do some other stuff and forget to initialise i

        i = addOne(i);           // Whoa! NPE!
    }

    public static int addOne(int i)
    {
        return i + 1;
    }
}

Đây chỉ là mã xấu và không liên quan gì đến autoboxing. Các biến iđược khởi tạo sớm. Hoặc làm cho nó thành một khai báo trống ( Integer i;) để trình biên dịch có thể chỉ ra rằng bạn quên khởi tạo nó, hoặc chờ khai báo cho đến khi bạn biết giá trị của nó.
erickson

Hmm, và nếu tôi làm một cái gì đó bên trong một khối thử bắt thì trình biên dịch sẽ buộc tôi khởi tạo nó bằng một cái gì đó. Đây không phải là mã thực - nó là một ví dụ về cách nó có thể xảy ra.
PEELY

Điều này chứng tỏ điều gì? Hoàn toàn không có lý do để sử dụng đối tượng Integer. Thay vào đó, bây giờ bạn phải đối phó với một NullPulum tiềm năng.
Richard Clayton
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.