我正在寻找一个清晰、简洁和准确的答案。

理想情况下作为实际答案,尽管欢迎链接到好的解释。

有帮助吗?

解决方案

盒装值是 数据结构 这是最小的包装 原始类型*。装箱值通常存储为指向对象的指针 .

因此,装箱值使用更多内存,并且至少需要两次内存查找才能访问:一次获取指针,另一次跟随该指针指向原语。显然,这不是您希望在内部循环中出现的情况。另一方面,盒装值通常与系统中的其他类型配合得更好。由于它们是该语言中的一流数据结构,因此它们具有其他数据结构所具有的预期元数据和结构。

在 Java 和 Haskell 中,泛型集合不能包含未装箱的值。.NET 中的通用集合可以保存未装箱的值,而不会造成任何损失。Java 的泛型仅用于编译时类型检查,.NET 将 为运行时实例化的每个泛型类型生成特定的类.

Java 和 Haskell 都有未装箱的数组,但它们明显不如其他集合方便。然而,当需要峰值性能时,为避免装箱和拆箱的开销而带来一些不便是值得的。

* 对于本讨论,原始值是任何可以存储在 调用堆栈, ,而不是存储为指向堆上值的指针。通常,这只是机器类型(整数、浮点数等)、结构体,有时还包括静态大小的数组。.NET-land 将它们称为值类型(而不是引用类型)。Java 人称它们为原始类型。哈斯凯利恩人只是称它们为“未装箱”。

** 在这个答案中我还关注 Java、Haskell 和 C#,因为这就是我所知道的。就其价值而言,Python、Ruby 和 Javascript 都有专门的盒装值。这也称为“一切皆对象”方法***。

*** 警告:在某些情况下,足够先进的编译器/JIT实际上可以检测到在查看源代码时在语义上装箱的值在运行时可以安全地成为未装箱的值。从本质上讲,由于出色的语言实现者,您的盒子有时是免费的。

其他提示

C# 3.0 简而言之:

拳击是将价值类型投入参考类型的行为:

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

拆箱是...相反:

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

装箱和拆箱是将原始值转换为面向对象的包装类(装箱),或将值从面向对象的包装类转换回原始值(拆箱)的过程。

例如,在java中,您可能需要转换 int 值转化为 Integer (拳击)如果你想将它存储在 Collection 因为基元不能存储在 Collection, ,仅对象。但当你想把它从 Collection 您可能希望获得以下值: int 而不是一个 Integer 所以你会把它拆箱。

装箱和拆箱并不是天生的 坏的, ,但这是一个权衡。根据语言实现的不同,它可能比仅使用原语更慢且占用更多内存。但是,它还可以允许您使用更高级别的数据结构并在代码中实现更大的灵活性。

如今,最常在 Java(和其他语言)的“自动装箱/自动拆箱”功能的上下文中讨论它。这里有一个 以java为中心的自动装箱解释.

在.Net中:

通常,您不能依赖函数将使用的变量类型,因此您需要使用从最低公分母扩展的对象变量 - 在 .Net 中,这是 object.

然而 object 是一个类并将其内容存储为引用。

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

虽然这两个列表都包含相同的信息,但第二个列表更大且更慢。第二个列表中的每个值实际上是对 object 持有 int.

这被称为盒装,因为 int 被包裹着 object. 。当它投射回来时 int 已拆箱 - 转换回其价值。

对于值类型(即全部 structs)这很慢,并且可能会占用更多空间。

对于参考类型(即全部 classes)这并不是什么问题,因为它们无论如何都被存储为参考。

装箱值类型的另一个问题是,您处理的是框而不是值并不明显。当你比较两个 structs 那么你正在比较值,但是当你比较两个 classes 然后(默认情况下)您正在比较参考 - 即这些是同一个实例吗?

在处理装箱值类型时,这可能会令人困惑:

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

解决方法很简单:

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

然而,在处理盒装值时需要注意的是另一件事。

.NET FCL 通用集合:

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

都是为了克服以前集合实现中装箱和拆箱的性能问题而设计的。

有关更多信息,请参阅第 16 章, 通过 C# 的 CLR(第二版).

装箱是将值类型转换为引用类型的过程。

拆箱是将引用类型转换为值类型。

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

值类型有:
int、char 和结构体、枚举。参考类型有:类、接口、数组、字符串和对象

装箱和拆箱有助于将值类型视为对象。装箱意味着将值转换为对象引用类型的实例。例如, Int 是一个类并且 int 是一种数据类型。转换 intInt 是拳击的一个例子,而转换 Intint 正在拆箱。这个概念有助于垃圾收集,另一方面,拆箱将对象类型转换为值类型。

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

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

与其他任何事情一样,如果不小心使用,自动装箱可能会出现问题。典型的情况是最终出现 NullPointerException 并且无法追踪它。即使使用调试器。尝试这个:

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;
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top