基本原理
其他答案是无知和/或误导性的。
序幕
首先阅读我在How do ValueTypes derive from Object (ReferenceType) and still be ValueTypes?上的回答有助于理解这一点
那么发生了什么?
System.Int32 是一个包含 32 位有符号整数的结构。它确实不包含自己。
要在 IL 中引用值类型,语法为 valuetype [assembly]Namespace.TypeName。
II.7.2 内置类型
CLI 内置类型在基类库中定义了相应的值类型。它们只能在签名中使用它们的特殊编码来引用(即,不使用通用的 valuetype TypeReference 语法)。分区 I 指定内置类型。
这意味着,如果您有一个采用 32 位整数的方法,则不能使用传统的 valuetype [mscorlib]System.Int32 语法,而是使用 32 位有符号内置整数 int32 的特殊编码。
在 C# 中,这意味着,无论您键入 System.Int32 还是 int,都将编译为 int32,不是 valuetype [mscorlib]System.Int32。
您可能听说过 int 是 C# 中 System.Int32 的别名,但实际上,两者都是内置 CLS 值类型 int32 的别名。
所以虽然像这样的结构
public struct MyStruct
{
internal MyStruct m_value;
}
确实会编译为(因此无效):
.class public sequential ansi sealed beforefieldinit MyStruct extends [mscorlib]System.ValueType
{
.field assembly valuetype MyStruct m_value;
}
namespace System
{
public struct Int32
{
internal int m_value;
}
}
而是编译成(忽略接口):
.class public sequential ansi sealed beforefieldinit System.Int32 extends [mscorlib]System.ValueType
{
.field assembly int32 m_value;
}
C# 编译器确实不需要需要特殊情况来编译System.Int32,因为CLI 规范规定所有对System.Int32 的引用都替换为内置CLS 值的特殊编码输入int32。
因此。 System.Int32 是一个不包含另一个 System.Int32 而是一个 int32 的结构。在 IL 中,您可以有 2 个方法重载,一个采用 System.Int32,另一个采用 int32 并让它们共存:
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89)
.ver 2:0:0:0
}
.assembly test {}
.module test.dll
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003
.corflags 0x00000001
.class MyNamespace.Program
{
.method static void Main() cil managed
{
.entrypoint
ldc.i4.5
call int32 MyNamespace.Program::Lol(valuetype [mscorlib]System.Int32) // Call the one taking the System.Int32 type.
call int32 MyNamespace.Program::Lol(int32) // Call the overload taking the built in int32 type.
call void [mscorlib]System.Console::Write(int32)
call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
pop
ret
}
.method static int32 Lol(valuetype [mscorlib]System.Int32 x) cil managed
{
ldarg.0
ldc.i4.1
add
ret
}
.method static int32 Lol(int32 x) cil managed
{
ldarg.0
ldc.i4.1
add
ret
}
}
ILSpy、dnSpy、.NET Reflector 等反编译器可能会产生误导。他们(在撰写本文时)将 int32 和 System.Int32 反编译为 C# 关键字 int 或类型 System.Int32,因为这就是我们在 C# 中定义整数的方式。
但int32 是 32 位有符号整数的内置值类型(即,VES 直接支持这些类型,带有 add、sub、ldc.i4.x 等指令); System.Int32 是类库中定义的对应值类型。
对应的System.Int32 类型用于装箱,以及ToString()、CompareTo() 等方法。
如果您使用纯 IL 编写程序,您绝对可以以完全相同的方式创建自己的包含 int32 的值类型,您仍然使用 int32 但在自定义“对应”上调用方法值类型。
.class MyNamespace.Program
{
.method hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 8
ldc.i4.0
call void MyNamespace.Program::PrintWhetherGreaterThanZero(int32)
ldc.i4.m1 // -1
call void MyNamespace.Program::PrintWhetherGreaterThanZero(int32)
ldc.i4.3
call void MyNamespace.Program::PrintWhetherGreaterThanZero(int32)
ret
}
.method private hidebysig static void PrintWhetherGreaterThanZero(int32 'value') cil managed noinlining
{
.maxstack 8
ldarga 0
call instance bool MyCoolInt32::IsGreaterThanZero()
brfalse.s printOtherMessage
ldstr "Value is greater than zero"
call void [mscorlib]System.Console::WriteLine(string)
ret
printOtherMessage:
ldstr "Value is not greater than zero"
call void [mscorlib]System.Console::WriteLine(string)
ret
}
}
.class public MyCoolInt32 extends [mscorlib]System.ValueType
{
.field assembly int32 myCoolIntsValue;
.method public hidebysig bool IsGreaterThanZero()
{
.maxstack 8
ldarg.0
ldind.i4
ldc.i4.0
bgt.s isNonZero
ldc.i4.0
ret
isNonZero:
ldc.i4.1
ret
}
}
这与System.Int32 类型没有什么不同,只是C# 编译器不会将MyCoolInt32 视为对应的int32 类型,但对于CLR,这无关紧要。然而,这将使 PEVerify.exe 失败,但它会运行得很好。
反编译器在反编译上述内容时会显示强制转换和明显的指针取消引用,因为它们也不认为 MyCoolInt32 和 int32 相关。
但在功能上并没有什么不同,而且 CLR 的幕后也没有什么魔法。