为什么在c#中需要装箱和拆箱?

我知道什么是装箱和开箱,但我不理解它的真正用途。为什么,在哪里使用?

short s = 25;

object objshort = s;  //Boxing

short anothershort = (short)objshort;  //Unboxing

当前回答

最后一个我不得不开箱的地方是从数据库中检索数据的代码(我没有使用LINQ to SQL,只是简单的旧ADO.NET):

int myIntValue = (int)reader["MyIntValue"];

基本上,如果使用泛型之前的旧api,就会遇到装箱。除此之外,这种情况并不常见。

其他回答

装箱是将值转换为引用类型,其中数据位于堆上对象的某个偏移量处。

至于拳击到底是做什么的。下面是一些例子

单C + +

void* mono_object_unbox (MonoObject *obj)
 {    
MONO_EXTERNAL_ONLY_GC_UNSAFE (void*, mono_object_unbox_internal (obj));
 }

#define MONO_EXTERNAL_ONLY_GC_UNSAFE(t, expr) \
    t result;       \
    MONO_ENTER_GC_UNSAFE;   \
    result = expr;      \
    MONO_EXIT_GC_UNSAFE;    \
    return result;

static inline gpointer
mono_object_unbox_internal (MonoObject *obj)
{
    /* add assert for valuetypes? */
    g_assert (m_class_is_valuetype (mono_object_class (obj)));
    return mono_object_get_data (obj);
}

static inline gpointer
mono_object_get_data (MonoObject *o)
{
    return (guint8*)o + MONO_ABI_SIZEOF (MonoObject);
}

#define MONO_ABI_SIZEOF(type) (MONO_STRUCT_SIZE (type))
#define MONO_STRUCT_SIZE(struct) MONO_SIZEOF_ ## struct
#define MONO_SIZEOF_MonoObject (2 * MONO_SIZEOF_gpointer)

typedef struct {
    MonoVTable *vtable;
    MonoThreadsSync *synchronisation;
} MonoObject;

在Mono中打开一个对象是一个在对象中2个gpointer偏移量处强制转换指针的过程(例如16字节)。gpointer是一个void*。在查看MonoObject的定义时,这是有意义的,因为它显然只是数据的头部。

C++

在c++中,你可以这样做:

#include <iostream>
#define Object void*

template<class T> Object box(T j){
  return new T(j);
}

template<class T> T unbox(Object j){
  T temp = *(T*)j;
  delete j;
  return temp;
}

int main() {
  int j=2;
  Object o = box(j);
  int k = unbox<int>(o);
  std::cout << k;
}

为什么

拥有统一的类型系统,并允许值类型以与引用类型完全不同的方式表示其基础数据(例如,int只是一个32位的桶,这与引用类型完全不同)。

这样想。你有一个object类型的变量o。现在你有一个int型,你想把它变成o。o是对某处某物的引用,而int型显然不是对某处某物的引用(毕竟,它只是一个数字)。所以,你要做的是:创建一个可以存储int的新对象,然后将该对象的引用赋值给o。我们称这个过程为“装箱”。

所以,如果你不关心是否有一个统一的类型系统(例如,引用类型和值类型有非常不同的表示,你不想用一种通用的方式来“表示”这两者),那么你就不需要装箱。如果你不关心用int表示它们的底层值(也就是说,用int作为引用类型,只存储对它们底层值的引用),那么你不需要装箱。

我应该在哪里使用它。

例如,旧的集合类型ArrayList只包含对象。也就是说,它只存储对某个地方的某个对象的引用。如果没有装箱,就不能在这样的集合中放入int型。但在拳击中,你可以。

现在,在泛型的时代,你并不真的需要这个,通常可以轻松地进行,而不需要考虑这个问题。但有一些注意事项需要注意:

这是正确的:

double e = 2.718281828459045;
int ee = (int)e;

这不是:

double e = 2.718281828459045;
object o = e; // box
int ee = (int)o; // runtime exception

相反,你必须这样做:

double e = 2.718281828459045;
object o = e; // box
int ee = (int)(double)o;

首先,我们必须显式地打开double ((double)o),然后将其强制转换为int型。

下面的结果是什么:

double e = 2.718281828459045;
double d = e;
object o1 = d;
object o2 = e;
Console.WriteLine(d == e);
Console.WriteLine(o1 == o2);

下句话之前先想一下。

如果你说真假太棒了!等等,什么?这是因为引用类型上的==使用引用相等来检查引用是否相等,而不是检查底层值是否相等。这是一个很容易犯的危险错误。也许更微妙

double e = 2.718281828459045;
object o1 = e;
object o2 = e;
Console.WriteLine(o1 == o2);

也会打印False!

更好的说法是:

Console.WriteLine(o1.Equals(o2));

然后,谢天谢地,它会打印True。

最后一个微妙之处:

[struct|class] Point {
    public int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

Point p = new Point(1, 1);
object o = p;
p.x = 2;
Console.WriteLine(((Point)o).x);

输出是什么?视情况而定!如果Point是一个结构体,则输出为1,但如果Point是一个类,则输出为2!装箱转换生成被装箱的值的副本,以解释行为上的差异。

最后一个我不得不开箱的地方是从数据库中检索数据的代码(我没有使用LINQ to SQL,只是简单的旧ADO.NET):

int myIntValue = (int)reader["MyIntValue"];

基本上,如果使用泛型之前的旧api,就会遇到装箱。除此之外,这种情况并不常见。

Boxing和Unboxing专门用于将值类型对象视为引用类型;将它们的实际值移动到托管堆并通过引用访问它们的值。

如果不进行装箱和拆箱,就无法通过引用传递值类型;这意味着您不能将值类型作为Object的实例传递。

In .net, every instance of Object, or any type derived therefrom, includes a data structure which contains information about its type. "Real" value types in .net do not contain any such information. To allow data in value types to be manipulated by routines that expect to receive types derived from object, the system automatically defines for each value type a corresponding class type with the same members and fields. Boxing creates a new instances of this class type, copying the fields from a value type instance. Unboxing copies the fields from an instance of the class type to an instance of the value type. All of the class types which are created from value types are derived from the ironically named class ValueType (which, despite its name, is actually a reference type).