有时会出现一个实例 值类型需要被视为一个 参考类型的实例。 For 像这样的情况,值类型 实例可以转换为 通过a引用类型实例 过程称为拳击。当一个值 类型实例是盒装,存储是 分配在堆上和 实例的值被复制到其中 空间。对此存储的引用是 放在堆栈上。盒装价值 是一个对象,一个引用类型 包含值的内容 类型实例。
在Wikipedia中有一个Java示例。但是在C#中,有哪些情况需要打包一个值类型?或者更好/类似的问题是,为什么人们想要在堆上(盒装)而不是堆栈上存储值类型?
答案 0 :(得分:14)
通常,您通常希望避免装箱值类型。
但是,很少有这种情况有用。例如,如果您需要定位1.1框架,则无法访问泛型集合。在.NET 1.1中使用集合需要将您的值类型视为System.Object,这会导致装箱/取消装箱。
在.NET 2.0+中仍然存在这种情况。只要您想利用所有类型(包括值类型)可以直接作为对象这一事实,您可能需要使用装箱/拆箱。这有时很方便,因为它允许您在集合中保存任何类型(通过在通用集合中使用对象而不是T),但一般来说,最好避免这种情况,因为您正在失去类型安全性。但是,经常发生拳击的一种情况是,当你使用反射时 - 反射中的许多调用在处理值类型时需要装箱/拆箱,因为事先不知道类型。
答案 1 :(得分:13)
几乎没有充分的理由故意选择值类型。包装值类型的原因几乎总是将其存储在一些非类型感知的集合中。例如,旧ArrayList是对象的集合,它们是引用类型。收集整数的唯一方法是将它们作为对象封装并将它们传递给ArrayList。
如今,我们有通用集合,所以这不是一个问题。
答案 2 :(得分:9)
拳击通常会在.NET中自动发生;通常当您将值类型传递给期望引用类型的东西时。一个常见的例子是string.Format()。将原始值类型传递给此方法时,它们将作为调用的一部分进行装箱。所以:
int x = 10;
string s = string.Format( "The value of x is {0}", x ); // x is boxed here
这说明了一个简单的场景,其中值类型(x)被自动装箱以传递给期望对象的方法。通常,您希望尽可能避免装箱值类型...但在某些情况下它非常有用。
有趣的是,当您在.NET中使用泛型时,当用作参数或类型成员时,值类型不会被加框。这使得泛型比旧的C#代码(例如ArrayList)更有效,它将{object}视为与类型无关的所有内容。这增加了使用通用集合的另一个原因,例如List<T>
或Dictionary<T,K>
或ArrayList
上的Hashtable
。
答案 3 :(得分:5)
我会推荐你2篇Eric Lippert的好文章
http://blogs.msdn.com/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx
以下是我100%同意的引用
将堆栈用于有价值的本地 类型只是一个优化 CLR代表您执行。 价值类型的相关特征是 他们有存在的语义 按价值复制,而不是有时复制 他们的释放可以通过优化 运行时。
在99%的应用程序中,开发人员不应该关心为什么Value类型在堆栈中而不在堆中,以及我们可以在这里获得什么样的性能。 Juts考虑到非常简单的规则:
任何规则都允许在特殊情况下排除,但不要尝试过度优化。
P.S。 我遇到了一些具有2 - 3年经验的ASP.NET开发人员,他们不知道堆栈和堆之间的区别。 :-(如果我是面试官,我不会雇用这样的人,但不是因为拳击/拆箱可能是我见过的任何ASP.NET网站的瓶颈。
答案 4 :(得分:2)
我认为在c#中装箱的一个很好的例子出现在像ArrayList这样的非通用集合中。
答案 5 :(得分:1)
一个例子是当一个方法接受一个object参数并且必须传入一个值类型时。
答案 6 :(得分:1)
以下是装箱/拆箱的一些例子
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
答案 7 :(得分:1)
发生这种情况的一种情况是,例如,如果您有方法期望类型为object的参数,并且您正在传递其中一种基本类型,例如int。或者,如果将参数定义为int类型的“ref”。
答案 8 :(得分:1)
代码
int x = 42;
Console.Writeline("The value of x is {0}", x );
实际上是盒子和取消装箱,因为Writeline
内部有一个int
。为了避免这种情况,你可以做到
int x = 42;
Console.Writeline("The value of x is {0}", x.ToString());
谨防微妙的错误!
您可以通过将自己的类型声明为struct
来声明自己的值类型。想象一下,您声明了一个包含大量属性的struct
,然后将一些实例放在ArrayList
中。这当然是他们的盒子。现在通过[]
运算符引用一个,将其转换为类型并设置属性。您只需在副本上设置属性即可。 ArrayList
中的那个仍未修改。
由于这个原因,值类型必须始终是不可变的,即使所有成员变量readonly
使得它们只能在构造函数中设置,并且没有任何可变类型作为成员。