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

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

short s = 25;

object objshort = s;  //Boxing

short anothershort = (short)objshort;  //Unboxing

当前回答

当我们有一个需要object作为参数的函数,但我们有不同的值类型需要传递时,装箱是必需的,在这种情况下,我们需要在将值类型传递给函数之前首先将值类型转换为对象数据类型。

我不认为这是真的,试试这个:

class Program
    {
        static void Main(string[] args)
        {
            int x = 4;
            test(x);
        }

        static void test(object o)
        {
            Console.WriteLine(o.ToString());
        }
    }

这运行得很好,我没有使用装箱/拆箱。(除非编译器在幕后做这些?)

其他回答

在. net框架中,有两种类型——值类型和引用类型。这在面向对象语言中比较常见。

面向对象语言的一个重要特性是以类型不可知的方式处理实例的能力。这被称为多态性。由于我们希望利用多态性,但我们有两种不同的类型,因此必须有某种方法将它们组合在一起,以便以相同的方式处理其中一个或另一个。

现在,回到过去(Microsoft.NET 1.0),还没有这种新奇的泛型喧闹。您不能编写一个方法,它只有一个参数,可以服务于值类型和引用类型。这违反了多态性。因此,采用装箱作为一种将值类型强制转换为对象的方法。

如果这是不可能的,那么框架中就会充斥着方法和类,它们的唯一目的就是接受其他类型。不仅如此,由于值类型并没有真正共享一个共同的类型祖先,因此必须为每种值类型(比特、字节、int16、int32等等)设置不同的方法重载。

拳击阻止了这种情况的发生。这就是英国人庆祝节礼日的原因。

当一个方法只接受一个引用类型作为参数时(比如一个泛型方法通过new约束被约束为一个类),您将不能将引用类型传递给它,而必须对它进行装箱。

对于任何以object作为参数的方法也是如此——这必须是引用类型。

装箱并不是你真正使用的东西——它是运行时使用的东西,这样你就可以在必要时以同样的方式处理引用和值类型。例如,如果您使用ArrayList来保存整数列表,则整数将被装箱以适应ArrayList中的对象类型插槽。

现在使用泛型集合,这种情况几乎消失了。如果你创建一个List<int>,没有装箱完成- List<int>可以直接保存整数。

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

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

单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;
}

当我们有一个需要object作为参数的函数,但我们有不同的值类型需要传递时,装箱是必需的,在这种情况下,我们需要在将值类型传递给函数之前首先将值类型转换为对象数据类型。

我不认为这是真的,试试这个:

class Program
    {
        static void Main(string[] args)
        {
            int x = 4;
            test(x);
        }

        static void test(object o)
        {
            Console.WriteLine(o.ToString());
        }
    }

这运行得很好,我没有使用装箱/拆箱。(除非编译器在幕后做这些?)