用例在C#中装箱值类型?

时间:2009-06-22 17:39:33

标签: c# boxing value-type reference-type cts

  

有时会出现一个实例   值类型需要被视为一个   参考类型的实例。 For   像这样的情况,值类型   实例可以转换为   通过a引用类型实例   过程称为拳击。当一个值   类型实例是盒装,存储是   分配在堆上和   实例的值被复制到其中   空间。对此存储的引用是   放在堆栈上。盒装价值   是一个对象,一个引用类型   包含值的内容   类型实例。

     

Understanding .NET's Common Type System

Wikipedia中有一个Java示例。但是在C#中,有哪些情况需要打包一个值类型?或者更好/类似的问题是,为什么人们想要在堆上(盒装)而不是堆栈上存储值类型?

9 个答案:

答案 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

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

以下是我100%同意的引用

  

将堆栈用于有价值的本地   类型只是一个优化   CLR代表您执行。   价值类型的相关特征是   他们有存在的语义   按价值复制,而不是有时复制   他们的释放可以通过优化   运行时。

在99%的应用程序中,开发人员不应该关心为什么Value类型在堆栈中而不在堆中,以及我们可以在这里获得什么样的性能。 Juts考虑到非常简单的规则:

  1. 如果没有,请避免装箱/拆箱 必要的,使用泛型集合。 大多数问题发生在你身上 定义你自己的类型,但是当你 正确使用现有类型 (由Microsoft或您的 同事)
  2. 制作您的价值类型 简单。如果你需要一个结构 有10-20个领域,我想你是 更好地创建一个类。想象一下,所有 每次都会复制这些字段 当你偶尔通过它时 按价值运作......
  3. 我觉得这不是很有用 引用类型的值类型 里面的字段。喜欢struct with 字符串和对象字段。
  4. 根据需要定义您需要的类型 所需的功能,而不是在哪里 它应该存储。结构有 比较有限的功能 类,所以如果struct不能提供 所需的功能,如 默认构造函数,定义类。
  5. 如果有什么可以执行任何事情 与其他人的数据一起行动 类型,通常定义为a 类。对于结构操作 应该定义不同的类型 只有你可以将一种类型转换为 另一个。假设您可以添加int double因为你可以将int转换为 双
  6. 如果某些事情应该是无国籍的,那就是一个阶级。
  7. 当您犹豫时,请使用参考类型。 : - )
  8. 任何规则都允许在特殊情况下排除,但不要尝试过度优化。

    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使得它们只能在构造函数中设置,并且没有任何可变类型作为成员。