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

在某些情况下,需要将值类型的实例视为引用类型的实例。 对于这种情况,可以通过名为boxing的过程将值类型实例转换为引用类型实例。 当盒式化值类型实例时,将在堆上分配存储,并将实例的值复制到该空间中。 对此存储的引用放在堆栈上。 盒装值是一个对象,一个包含值类型实例内容的引用类型。

了解.NET的通用类型系统

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

通常,您通常希望避免装箱值类型。

但是,很少有这种情况有用。 例如,如果您需要定位1.1框架,则无法访问generics集合。 在.NET 1.1中对集合的任何使用都需要将您的值类型视为System.Object,这会导致装箱/取消装箱。

在.NET 2.0+中,这仍然有用。 只要您想利用所有类型(包括值类型)可以直接视为对象的事实,您可能需要使用装箱/拆箱。 这有时很方便,因为它允许您在集合中保存任何类型(通过在通用集合中使用对象而不是T),但一般来说,最好避免这种情况,因为您正在失去类型安全性。 但是,经常发生装箱的一种情况是,当你使用reflection时 – reflection中的许多调用在处理值类型时需要装箱/拆箱,因为事先不知道类型。

几乎从来没有一个充分的理由故意选择值类型。 包装值类型的原因几乎总是将其存储在一些非类型感知的集合中。 例如,旧的ArrayList是对象的集合,它们是引用类型。 收集整数的唯一方法是将它们作为对象封装并将它们传递给ArrayList。

如今,我们有通用的集合,所以这不是一个问题。

拳击通常在必要时在.NET中自动发生; 通常当您将值类型传递给期望引用类型的东西时。 一个常见的例子是string.Format()。 将原始值类型传递给此方法时,它们将作为调用的一部分进行装箱。 所以:

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

这说明了一个简单的场景,其中值类型(x)被自动装箱以传递给期望对象的方法。 通常,您希望尽可能避免装箱值类型…但在某些情况下它非常有用。

有趣的是,当您在.NET中使用generics时,当用作参数或类型成员时,值类型不会被加框。 这使得generics比旧的C#代码(例如ArrayList)更有效,它将{object}视为与类型无关的所有内容。 这增加了使用generics集合的另一个原因,例如ListDictionary over ArrayListHashtable

我会推荐你​​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. 在没有必要时避免装箱/拆箱,使用generics集合。 大多数问题不是在您定义自己的类型时发生的,而是在您正确使用现有类型时(由Microsoft或您的同事定义)
  2. 使您的价值类型变得简单。 如果你需要一个包含10-20个字段的结构,我想你最好创建一个类。 想象一下,每当你偶尔通过值传递一个函数时,所有这些字段都会被复制……
  3. 我认为在其中包含引用类型字段的值类型并不是非常有用。 就像带有String和对象字段的struct一样。
  4. 根据所需function定义所需的类型,而不是存储位置。 与类相比,结构具有有限的function,因此如果struct无法提供所需的function,例如默认构造函数,则定义类。
  5. 如果某些东西可以对其他类型的数据执行任何操作,则通常将其定义为类。 对于结构体,只有在可以将一种类型转换为另一种类型时,才应定义具有不同类型的操作。 假设您可以将int添加到double,因为您可以将int转换为double。
  6. 如果某事应该是无国籍的,那就是一个阶级。
  7. 当您犹豫时,请使用引用类型。 🙂

任何规则都允许在特殊情况下排除,但不要尝试过度优化。

ps我遇到了一些具有2 – 3年经验的ASP.NET开发人员,他们不知道堆栈和堆之间的区别。 :-(如果我是面试官,我不会雇用这样的人,但不是因为拳击/拆箱可能是我见过的任何ASP.NET网站的瓶颈。

我认为c#中装箱的一个很好的例子出现在像ArrayList这样的非generics集合中。

一个示例是当方法采用对象参数并且必须传入值类型时。

下面是装箱/拆箱的一些例子

 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 

发生这种情况的一种情况是,例如,如果您有方法期望类型为object的参数,并且您正在传递其中一种基本类型,例如int。 或者,如果将参数定义为int类型的’ref’。

代码

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

实际上是box和unboxes,因为Writeline在内部进行了int 。 为了避免这种情况你可以做到

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

小心微妙的错误!

您可以通过将自己的类型声明为struct来声明自己的值类型。 想象一下,您声明了一个包含许多属性的struct ,然后将一些实例放在ArrayList 。 这当然是他们的盒子。 现在通过[]运算符引用一个,将其转换为类型并设置属性。 您只需在副本上设置属性即可。 ArrayList中的那个仍未修改。

因此,值类型必须始终是不可变的,即使所有成员变量readonly这样它们只能在构造函数中设置,并且没有任何可变类型作为成员。