欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C# 基础之装拆箱 IL 层

程序员文章站 2024-03-19 09:00:04
...

C# 基础之装拆箱

装箱:当一个值类型转成引用类型,我们可以看成 我们把 1元钱装入钱包中,此时发生装箱操作。

拆箱:当一个引用类型转成值类型,我们可以看成 从钱包中把 1元钱 提取出来。

以上个人通俗白话的理解。

测试案例:
C# 基础之装拆箱 IL 层
这里我声明了一个引用类型和值类型。

C# 基础之装拆箱 IL 层

上面是发生装拆箱的代码,很容易看清楚之间的类型转换后的装拆箱,不过我们从底层IL语言来看。

我们先看看装箱操作做了什么事情。

C# 基础之装拆箱 IL 层
我们可以看的 IL 先入栈,之后发生装箱的操作,之后把计算堆栈的推送给静态字段(赋值)

再看看拆箱操作的IL。

C# 基础之装拆箱 IL 层
IL 基本一样除了中间的 unbox il指令不一样以外。

那么该如何避免装拆箱?答案:泛型

        static int i = 1;
        static object i2 = new object();
        static void Main(string[] args)
        {
            Box<int>(i);

            BoxRef<object>(i2);
        }

        //值类型转成引用类型 装箱
        static void Box<T1>(T1 it) where T1 : struct
        {
            T1 ol;
            ol = it;
        }

        static void BoxRef<T1>(T1 it) where T1 : class
        {
            T1 ol;
            ol = it;
        }

上述代码拆分值类型与引用类型的泛型(String 特殊)

接着我们看看IL代码

// Token: 0x02000002 RID: 2
.class public auto ansi beforefieldinit ClassLibrary6.Class1
	extends [mscorlib]System.Object
{
	// Fields
	// Token: 0x04000001 RID: 1
	.field private static int32 i
	// Token: 0x04000002 RID: 2
	.field private static object i2

	// Methods
	// Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
	.method private hidebysig static 
		void Main (
			string[] args
		) cil managed 
	{
		// Header Size: 1 byte
		// Code Size: 24 (0x18) bytes
		.maxstack 8

		/* (16,9)-(16,10) C:\Users\admin\source\repos\ClassLibrary6\ClassLibrary6\Class1.cs */
		/* 0x00000251 00           */ IL_0000: nop
		/* (17,13)-(17,25) C:\Users\admin\source\repos\ClassLibrary6\ClassLibrary6\Class1.cs */
		/* 0x00000252 7E01000004   */ IL_0001: ldsfld    int32 ClassLibrary6.Class1::i
		/* 0x00000257 280100002B   */ IL_0006: call      void ClassLibrary6.Class1::Box<int32>(!!0)
		/* 0x0000025C 00           */ IL_000B: nop
		/* (19,13)-(19,32) C:\Users\admin\source\repos\ClassLibrary6\ClassLibrary6\Class1.cs */
		/* 0x0000025D 7E02000004   */ IL_000C: ldsfld    object ClassLibrary6.Class1::i2
		/* 0x00000262 280200002B   */ IL_0011: call      void ClassLibrary6.Class1::BoxRef<object>(!!0)
		/* 0x00000267 00           */ IL_0016: nop
		/* (20,9)-(20,10) C:\Users\admin\source\repos\ClassLibrary6\ClassLibrary6\Class1.cs */
		/* 0x00000268 2A           */ IL_0017: ret
	} // end of method Class1::Main

	// Token: 0x06000002 RID: 2 RVA: 0x0000206C File Offset: 0x0000026C
	.method private hidebysig static 
		void Box<valuetype .ctor ([mscorlib]System.ValueType) T1> (
			!!T1 it
		) cil managed 
	{
		// Header Size: 12 bytes
		// Code Size: 4 (0x4) bytes
		// LocalVarSig Token: 0x11000001 RID: 1
		.maxstack 1
		.locals init (
			[0] !!T1 ol
		)

		/* (24,9)-(24,10) C:\Users\admin\source\repos\ClassLibrary6\ClassLibrary6\Class1.cs */
		/* 0x00000278 00           */ IL_0000: nop
		/* (26,13)-(26,21) C:\Users\admin\source\repos\ClassLibrary6\ClassLibrary6\Class1.cs */
		/* 0x00000279 02           */ IL_0001: ldarg.0
		/* 0x0000027A 0A           */ IL_0002: stloc.0
		/* (27,9)-(27,10) C:\Users\admin\source\repos\ClassLibrary6\ClassLibrary6\Class1.cs */
		/* 0x0000027B 2A           */ IL_0003: ret
	} // end of method Class1::Box

	// Token: 0x06000003 RID: 3 RVA: 0x0000207C File Offset: 0x0000027C
	.method private hidebysig static 
		void BoxRef<class T1> (
			!!T1 it
		) cil managed 
	{
		// Header Size: 12 bytes
		// Code Size: 4 (0x4) bytes
		// LocalVarSig Token: 0x11000001 RID: 1
		.maxstack 1
		.locals init (
			[0] !!T1 ol
		)

		/* (30,9)-(30,10) C:\Users\admin\source\repos\ClassLibrary6\ClassLibrary6\Class1.cs */
		/* 0x00000288 00           */ IL_0000: nop
		/* (32,13)-(32,21) C:\Users\admin\source\repos\ClassLibrary6\ClassLibrary6\Class1.cs */
		/* 0x00000289 02           */ IL_0001: ldarg.0
		/* 0x0000028A 0A           */ IL_0002: stloc.0
		/* (33,9)-(33,10) C:\Users\admin\source\repos\ClassLibrary6\ClassLibrary6\Class1.cs */
		/* 0x0000028B 2A           */ IL_0003: ret
	} // end of method Class1::BoxRef

	// Token: 0x06000004 RID: 4 RVA: 0x0000208C File Offset: 0x0000028C
	.method public hidebysig specialname rtspecialname 
		instance void .ctor () cil managed 
	{
		// Header Size: 1 byte
		// Code Size: 8 (0x8) bytes
		.maxstack 8

		/* 0x0000028D 02           */ IL_0000: ldarg.0
		/* 0x0000028E 280F00000A   */ IL_0001: call      instance void [mscorlib]System.Object::.ctor()
		/* 0x00000293 00           */ IL_0006: nop
		/* 0x00000294 2A           */ IL_0007: ret
	} // end of method Class1::.ctor

	// Token: 0x06000005 RID: 5 RVA: 0x00002095 File Offset: 0x00000295
	.method private hidebysig specialname rtspecialname static 
		void .cctor () cil managed 
	{
		// Header Size: 1 byte
		// Code Size: 17 (0x11) bytes
		.maxstack 8

		/* (13,9)-(13,26) C:\Users\admin\source\repos\ClassLibrary6\ClassLibrary6\Class1.cs */
		/* 0x00000296 17           */ IL_0000: ldc.i4.1
		/* 0x00000297 8001000004   */ IL_0001: stsfld    int32 ClassLibrary6.Class1::i
		/* (14,9)-(14,41) C:\Users\admin\source\repos\ClassLibrary6\ClassLibrary6\Class1.cs */
		/* 0x0000029C 730F00000A   */ IL_0006: newobj    instance void [mscorlib]System.Object::.ctor()
		/* 0x000002A1 8002000004   */ IL_000B: stsfld    object ClassLibrary6.Class1::i2
		/* 0x000002A6 2A           */ IL_0010: ret
	} // end of method Class1::.cctor

} // end of class ClassLibrary6.Class1

C# 基础之装拆箱 IL 层

没有发生装拆箱,是之间call调用了对于的函数。

C# 基础之装拆箱 IL 层
我们发现它只是把 0号位置的参数推入计算堆栈并弹出,其他并没什么了。

就有点像C++的内联函数一样,提前生成对应的代码段,通过占位的方式避免装拆箱(不知道这么解释对不对)

总结几点:
1.装拆箱代IL码量少,但是太多性能低下。
2.泛型IL代码量多,但是避免了装拆箱,在目前的PC和移动平台下,基本可以不用考虑(内存换性能)

相关标签: C# 装拆箱