2009-06-22 23 views
10

Có trường hợp khi một thể hiện của loại giá trị cần được coi là trường hợp của loại tham chiếu. Đối với trường hợp như thế này, một kiểu giá trị thể hiện có thể được chuyển đổi thành cá thể loại tham chiếu thông qua quy trình được gọi là quyền anh. Khi giá trị loại loại được đóng hộp, dung lượng lưu trữ là được phân bổ trên heap và giá trị của ví dụ được sao chép vào không gian đó. Tham chiếu đến bộ nhớ này là được đặt trên ngăn xếp. Giá trị được đóng gói là một đối tượng, loại tham chiếu chứa nội dung của trường hợp loại giá trị.Trường hợp sử dụng cho boxing một loại giá trị trong C#?

Understanding .NET's Common Type System

Trong Wikipedia có là một ví dụ cho Java. Nhưng trong C#, một số trường hợp mà một trong những sẽ phải hộp một loại giá trị là gì? Hoặc sẽ là một câu hỏi tốt hơn/tương tự được, tại sao một người muốn lưu trữ một loại giá trị trên heap (đóng hộp) hơn là trên ngăn xếp?

+0

Xem thêm [boxing-occurrence-in-c-sharp] (http://stackoverflow.com/questions/7995606) – nawfal

Trả lời

13

Nói chung, thông thường, bạn sẽ muốn tránh đấm bốc các loại giá trị của mình.

Tuy nhiên, có những trường hợp hiếm gặp khi điều này hữu ích. Ví dụ: nếu bạn cần nhắm mục tiêu khuôn khổ 1.1, bạn sẽ không có quyền truy cập vào các bộ sưu tập chung. Bất kỳ việc sử dụng các bộ sưu tập trong .NET 1.1 sẽ yêu cầu xử lý loại giá trị của bạn như là một System.Object, gây ra boxing/unboxing.

Vẫn còn các trường hợp để điều này hữu ích trong .NET 2.0+. Bất cứ lúc nào bạn muốn tận dụng lợi thế của thực tế là tất cả các loại, bao gồm các loại giá trị, có thể được coi là một đối tượng trực tiếp, bạn có thể cần phải sử dụng boxing/unboxing. Điều này có thể hữu ích ở lần, vì nó cho phép bạn lưu bất kỳ loại nào trong một bộ sưu tập (bằng cách sử dụng đối tượng thay vì T trong một bộ sưu tập chung), nhưng nói chung, tốt hơn là tránh điều này, vì bạn đang mất an toàn kiểu. Tuy nhiên, một trường hợp thường xuyên xảy ra khi bạn sử dụng Reflection - nhiều cuộc gọi trong sự phản chiếu sẽ yêu cầu boxing/unboxing khi làm việc với các kiểu giá trị, vì kiểu đó không được biết trước.

13

Gần như không bao giờ có lý do chính đáng để cố tình gắn một loại giá trị. Hầu như luôn luôn, lý do để hộp một loại giá trị là để lưu trữ nó trong một số bộ sưu tập mà không phải là loại nhận thức. Ví dụ: ArrayList cũ là một tập hợp các đối tượng, là các loại tham chiếu. Cách duy nhất để thu thập, nói, số nguyên, là để hộp chúng như là đối tượng và vượt qua chúng để ArrayList.

Hiện tại, chúng tôi có các bộ sưu tập chung, vì vậy đây là vấn đề ít.

+4

Phản ánh vẫn buộc bạn vào hộp/giá trị unbox và rất có giá trị ... –

+7

Rất đúng . Tuy nhiên, sự phản chiếu là thứ bạn muốn giới hạn ở "Tôi hoàn toàn không thể làm điều này bằng bất kỳ cách nào khác", vì vậy tôi nghĩ câu đầu tiên của tôi là tốt. – Randolpho

2

Tôi nghĩ một ví dụ điển hình về quyền anh trong C# xảy ra trong các bộ sưu tập phi chung chung như ArrayList.

1

Một ví dụ sẽ khi một phương pháp có một tham số đối tượng và một loại giá trị phải được thông qua trong.

1

Dưới đây là một số ví dụ về boxing/unboxing

ArrayList ints = new ArrayList(); 
myInts.Add(1); // boxing 
myInts.Add(2); // boxing 

int myInt = (int)ints [0]; // unboxing 

Console.Write("Value is {0}", myInt); // boxing 
+1

Ví dụ! = Trường hợp sử dụng –

1

Một trong những tình huống khi điều này xảy ra được ví dụ nếu bạn có phương thức mong đợi tham số của đối tượng kiểu và bạn đang truyền vào một trong các kiểu nguyên thủy, ví dụ int. Hoặc nếu bạn xác định tham số là 'ref' của kiểu int.

+1

-1. Không có boxing trong C# khi bạn vượt qua bằng cách tham khảo. Xem ghi chú tại đây: http://msdn.microsoft.com/en-us/library/14akc2c7.aspx – Randolpho

+0

+1 từ tôi. Mỗi ngày một cái gì đó mới để học hỏi. – epitka

9

Quyền anh thường tự động diễn ra trong .NET khi họ phải; thường khi bạn chuyển một loại giá trị cho một cái gì đó mà mong đợi một loại tham chiếu. Một ví dụ phổ biến là string.Format(). Khi bạn chuyển các kiểu giá trị nguyên thủy cho phương thức này, chúng được đóng hộp như một phần của cuộc gọi. Vì vậy:

int x = 10; 
string s = string.Format("The value of x is {0}", x); // x is boxed here 

Điều này minh họa một trường hợp đơn giản trong đó loại giá trị (x) được tự động chuyển thành phương thức mong đợi một đối tượng. Nói chung, bạn muốn tránh các loại giá trị đấm bốc khi có thể ... nhưng trong một số trường hợp, nó rất hữu ích.

Ngoài thú vị, khi bạn sử dụng Generics trong .NET, các loại giá trị không được đóng hộp khi được sử dụng làm tham số hoặc thành viên của loại. Mà làm cho generics hiệu quả hơn so với mã C# cũ hơn (chẳng hạn như ArrayList) mà đối xử với tất cả mọi thứ như {object} để được loại bất khả tri. Điều này bổ sung thêm một lý do nữa để sử dụng các bộ sưu tập chung chung, như List<T> hoặc Dictionary<T,K> trên ArrayList hoặc Hashtable.

5

Tôi muốn giới thiệu cho bạn 2 điều tốt đẹp của Eric Lippert

http://blogs.msdn.com/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx

http://blogs.msdn.com/ericlippert/archive/2009/05/04/the-stack-is-an-implementation-detail-part-two.aspx

Dưới đây là trích đoạn mà tôi sẽ 100% đồng ý với

Sử dụng ngăn xếp cho người dân địa phương giá trị loại chỉ là một tối ưu hóa mà CLR thực hiện thay cho bạn. Tính năng liên quan của các loại giá trị là rằng chúng có ngữ nghĩa là được sao chép theo giá trị, không phải đôi khi vị trí thỏa thuận của chúng có thể được tối ưu hóa theo thời gian chạy là .

Trong 99% ứng dụng, nhà phát triển không nên quan tâm đến lý do tại sao loại giá trị nằm trong ngăn xếp chứ không phải trong heap và chúng tôi có thể đạt được hiệu suất gì ở đây. Juts có những quy tắc rất đơn giản:

  1. Tránh quyền anh/unboxing khi không cần thiết, sử dụng bộ sưu tập generics. Hầu hết các vấn đề không xảy ra khi bạn xác định các loại của riêng bạn, nhưng khi bạn sử dụng các loại hiện inproperly (được định nghĩa bởi Microsoft hoặc đồng nghiệp của bạn)
  2. Hãy kiểu giá trị của bạn đơn giản. Nếu bạn cần phải có cấu trúc với 10-20 trường, tôi cho rằng bạn sẽ tạo một lớp học tốt hơn cho số. Hãy tưởng tượng, tất cả rằng các trường sẽ được sao chép mỗi lần khi bạn thỉnh thoảng vượt qua nó một chức năng theo giá trị ...
  3. Tôi không nghĩ rằng nó rất hữu ích để có kiểu giá trị với loại tài liệu tham khảo lĩnh vực bên trong. Giống như struct với Trường chuỗi và đối tượng.
  4. Xác định loại bạn cần tùy thuộc vào chức năng yêu cầu , không phải ở nơi cần được lưu trữ. Các cấu trúc có chức năng giới hạn so với các lớp , vì vậy nếu cấu trúc không thể cung cấp chức năng bắt buộc, như hàm tạo mặc định, hãy xác định lớp.
  5. Nếu có điều gì đó có thể thực hiện bất kỳ hành động nào với dữ liệu của các loại khác, nó thường được định nghĩa là lớp . Đối với các cấu trúc hoạt động với , các loại khác nhau sẽ chỉ được xác định chỉ khi bạn có thể truyền một loại thành một loại khác. Giả sử bạn có thể thêm int vào số gấp đôi vì bạn có thể truyền int thành đúp.
  6. Nếu một thứ gì đó không có quốc tịch, đó là một lớp học.
  7. Khi bạn đang do dự, hãy sử dụng các loại tham chiếu. :-)

Bất kỳ quy tắc nào đều cho phép loại trừ trong trường hợp đặc biệt nhưng không cố gắng tối ưu hóa quá mức.

p.s. Tôi đã gặp một số nhà phát triển ASP.NET với 2-3 năm kinh nghiệm, những người không biết sự khác biệt giữa ngăn xếp và đống. :-(tôi sẽ không thuê một người như vậy nếu tôi là một người phỏng vấn, nhưng không phải vì boxing/unboxing có thể là một nút cổ chai trong bất kỳ trang web ASP.NET mà tôi từng thấy.

+0

Câu trả lời hay. +1 Tôi đồng ý; nếu các nhà phát triển không biết sự khác biệt giữa chồng và đống, nó làm nổi bật sự thiếu hiểu biết thực sự về các nguyên tắc cơ bản về cách dữ liệu của họ được lưu trữ và chỉ ra. Điều này có thể khiến họ đưa ra các lỗi nghiêm trọng (giả sử rằng việc thay đổi một đối tượng trong một bộ sưu tập không thay đổi nó trong một bộ sưu tập khác, ví dụ). – StriplingWarrior

+0

Cảm ơn các bài viết được liên kết! – felideon

+1

Ý bạn là gì ở số 3? tại sao điều đó lại quan trọng? và bạn có thể định nghĩa "không quốc tịch" ở mức 6 không? – MasterMastic

1

int x = 42; 
Console.Writeline("The value of x is {0}", x); 

thực hộp và unboxes vì ​​Writeline làm một diễn viên int bên trong. Để tránh điều này bạn có thể làm

int x = 42; 
Console.Writeline("The value of x is {0}", x.ToString()); 

Cảnh giác với những lỗi vi tế!

Bạn có thể khai báo các loại giá trị của riêng mình bằng cách khai báo loại của riêng bạn là struct. Hãy tưởng tượng bạn khai báo struct với nhiều thuộc tính và sau đó đặt một số phiên bản bên trong một ArrayList. Điều này hộp chúng tất nhiên. Bây giờ, hãy tham khảo một tài khoản thông qua toán tử [], truyền nó đến loại và đặt thuộc tính. Bạn chỉ cần đặt thuộc tính trên sao chép. Một trong số ArrayList vẫn chưa được sửa đổi. Vì lý do này, các loại giá trị phải luôn là bất biến, tức là tạo tất cả biến thành viên readonly sao cho chúng chỉ có thể được đặt trong hàm tạo và không có bất kỳ loại biến thể nào làm thành viên.

+0

Đây là những gì người ta có thể gọi là "tối ưu hóa vi mô" và tôi khuyên bạn nên tránh cách thức mã hóa giống như được hiển thị trong khối mã thứ hai. Trong thực tế ở đây thay vì boxing bạn tạo một chuỗi mới và sau đó chuỗi này được sao chép, bởi vì chuỗi là bất biến và khi bạn vượt qua nó như ByVal paremeter, nó cần được sao chép.Vì vậy, bạn có thực sự nghĩ rằng mã này sẽ tối ưu hơn không? Tôi nghi ngờ như vậy. Nếu Ms tạo ra một chức năng cơ bản Console.Writeline (định dạng chuỗi, int i); chấp nhận tham số nguyên, bạn nên sử dụng nó. –

+0

Nó không phải là một tối ưu hóa ở tất cả, nó chỉ minh họa nguyên tắc. Bạn có ý nghĩa gì bởi "nó nên được sao chép"? –

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