C# Int32: m_value

前端 未结 3 1201
北荒
北荒 2020-12-11 21:31

After reading a bit about the Int32 struct in C#, I realized that int and Int32 are synonymous. In the source code of Int32 struct, th

3条回答
  •  一生所求
    2020-12-11 21:59

    int in C# represents the same thing as int32 in CIL, which is a 4-byte primitive generally treated as a signed number. (Though CIL can do unsigned operations on it without a cast).

    It's one of the lowest-level building blocks from which we can go on to create more complicated structures and classes.

    But as such, it doesn't have any methods defined on it.

    System.Int32 meanwhile looks pretty much like a struct that wraps an int/int32 and does provide some methods.

    Let's consider it as that; let's think about what it would be like in a world without int being aliased with System.Int32:

    In this hypothetical situation, we would only be allowed to use the methods System.Int32 provides if we treated it as a special "boxed int" type, creating a System.Int32 from an int when we needed it, and extracting the int back again when we needed that.

    So, without aliasing to do (3).CompareTo(2) we would have to do:

    new System.Int32{m_value = 3}.CompareTo(2)
    

    But consider that the in-memory representation of int is 4 bytes and the in-memory representation of System.Int32 is the same 4 bytes. If we didn't have a strong type-system that barred considering one type as another type we could just treat one as the other whenever we wanted.

    Now, C# does not allow us to do this. E.g. we can't do:

    public struct MyInt32
    {
      private int _value;
    }
    /* … */
    MyInt32 = 3;
    

    We would need to add a cast method that would be called, or else C# will just refuse to work on it like this.

    CIL though has no such rule. It can just treat one type as another layout-compatible type whenever it wants. So the IL for (3).CompareTo(2) is:

    ldc.i4.3 // Push the 32-bit integer 3 on the stack.
    ldc.i4.2 // Push the 32-bit integer 2 on the stack.
    call instance int32 [mscorlib]System.Int32::CompareTo(int32)
    

    The call at the end just assumes that the 3 is a System.Int32 and calls it.

    This breaks the rules of C# type-safety, but those rules are not CIL's rules. The C# compiler also doesn't have to follow all of the rules that it enforces.

    So there's no need to put anything into m_value, we just say "oh those four bytes there, they're the m_value field of a System.Int32", and so it is magically done. (If you know C or C++ consider what would happen if you had two structs with equivalent members and cast a pointer to one of those types to void* and then back to a pointer of another. It's a bad practice and IIRC undefined rather than guaranteed, but the lower-level code is allowed to do those sort of things).

    And that is how aliasing works; .Net languages' compilers special-case the cases where we need to call a method on a primitive to do this sort of type-coercion that C# code itself does not allow.

    Likewise, it special cases the fact that a value-type cannot hold a field of its own type, and allows System.Int32 to have an int field, though generally struct S { public S value; } would not be allowed.

提交回复
热议问题