代码之家  ›  专栏  ›  技术社区  ›  zneak

System.Int32包含…另一个System.Int32

  •  14
  • zneak  · 技术社区  · 15 年前

    我用反省来检查 System.Int32 发现里面还有一个 系统.Int32 .

    System.Int32 m_value;
    

    这个 int 内景 m_value 字段中,可以有效地更改整数的值:

    object testInt = 4;
    Console.WriteLine(testInt); // yields 4
    
    typeof(System.Int32)
        .GetField("m_value", BindingFlags.NonPublic | BindingFlags.Instance)
        .SetValue(testInt, 5);
    Console.WriteLine(testInt); // yields 5
    

    3 回复  |  直到 8 年前
        1
  •  3
  •   Hans Passant    15 年前

    如前所述,32位整数可以有两种类型。内存或CPU寄存器(不仅仅是堆栈)中的任意四个字节,即fast版本。它可以嵌入到System.Object中,即盒装版本。System.Int32的声明与后者兼容。装箱时,它有一个典型的对象头,后跟存储值的4个字节。这4个字节正好映射到mèu值成员。也许你明白为什么这里没有冲突了:mïu值是 总是

    语言编译器和JIT编译器都非常清楚Int32的属性。编译器负责决定何时需要对整数进行装箱和解装箱,它会生成相应的IL指令。它知道有哪些IL指令可用于操作整数,而无需先对其装箱。从System.Int32实现的方法中显而易见,例如,它没有运算符==()的重写。这是由CEQ操作码完成的。但是它确实有一个Equals()的重写,在整型被装箱时重写Object.Equals()方法是必需的。你的编译器需要有同样的意识。

        2
  •  1
  •   Jason    15 年前

    thread 对这个谜进行艰苦的讨论。

        3
  •  0
  •   JSBÕ±Õ¸Õ£Õ¹    15 年前

    魔术其实是在拳击/拆箱。

    System.Int32 (及其别名) int 值类型 ,这意味着它通常在堆栈上分配。CLR接收你的 系统.Int32 声明并将其转换为32位的堆栈空间。

    object testInt = 4; ,编译器会自动将您的值装箱 4 变成一个 参考 ,自 object 是引用类型。你所拥有的是一个指向 系统.Int32 系统.Int32 .

    您的代码示例所做的是创建 参考 价值 系统.Int32 它所指的。这就解释了这种奇怪的行为。

        4
  •  0
  •   MulleDK19    6 年前

    理论基础

    其他答案是无知和/或误导。

    首先阅读我的答案可以帮助理解这一点 How do ValueTypes derive from Object (ReferenceType) and still be ValueTypes?

    怎么回事?

    System.Int32 是包含32位有符号整数的结构。是的 克制自己。 要引用IL中的值类型,语法为 valuetype [assembly]Namespace.TypeName

    二、 7.2内置类型

    这意味着,如果您有一个采用32位整数的方法,则不能使用常规的 valuetype [mscorlib]System.Int32 语法,但是32位有符号内置整数的特殊编码 int32

    在C#中,这意味着 系统.Int32 int 国际32 值类型[mscorlib]System.Int32

    内景 系统.Int32 二者都 是内置CLS值类型的别名 国际32 .

    所以当一个结构像

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

    需要一个特殊的案例来编译 系统.Int32 ,因为CLI规范规定 国际32 . 因此。 系统.Int32 但是一个 国际32 . 在IL中,可以有两个方法重载,一个采用 系统.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等反编译器可能会产生误导。他们(在撰写本文时)将反编译 国际32 系统.Int32 作为C#关键字 内景 或者类型 系统.Int32 ,因为这就是我们在C#中定义整数的方法。

    国际32 是32位有符号整数的内置值类型(即,VES直接支持这些类型,指令如下 add sub ldc.i4.x 等); 类库中定义的对应值类型。 系统.Int32 类型用于装箱和类似的方法 ToString() , CompareTo()

    如果用纯IL编写程序,则完全可以创建自己的值类型,其中包含 国际32 同样的方法,你还在用 国际32

    .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
        }
    }
    

    类型,C编译器不考虑的除外 MyCoolInt32 国际32 但是对于CLR来说,这并不重要。不过,这将导致PEVerify.exe失败,但运行正常。 国际32

    但在功能上,没有区别,在CLR的幕后也没有魔法。