Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to create System.Int32 in C#?

Tags:

c#

clr

If you get under the hood, value types in C# are treated very specially by compiler/CLR. But types internal to CLR are treated even more specially. Here is what I mean:

int a = 5;
int b = 10;
int с = a + b;
a.CompareTo(b);

You can hover with your mouse over int in Visual Studio and see that actually it is System.Int32 struct. That's OK. Now you can grab ILDasm and look into what System.Int32 is: turns over that it is very simple struct with one field of type int32 (this is int32 internal to CLR) and there is no overloaded operator for addition.

So, how this int с = a + b works then? Again I grab ILDasm and look into IL. Turns out that there is no System.Int32 in IL code: compiler automatically understands that it should replace it with int32. And there is IL instruction add that works for couple of int32 on a stack. What amuses me even more, CLR allows to call instance methods for System.Int32 on int32. Looks like some black magic to me.

So, here goes the pure theoretical question: seems that System.Int32 is type like any other, could it be created in C# somehow? And if it could, could you do anything useful with it (actual int32 field is private)?

Edit: Ok, to make it a bit more clear: this question have nothing about int being alias to System.Int32. One can take provided example, replace int with System.Int32 and skip first paragraph after the example. The real question is about possibility to have valuetype [mscorlib]System.Int32 a in your IL code instead of just int32 a.

like image 235
iw.kuchin Avatar asked Oct 26 '12 06:10

iw.kuchin


1 Answers

So, consider the following code:

    static void Main(string[] args)
    {
        int x = 5;
        Print(x);
        Console.ReadLine();
    }

    static void Print(object x)
    {
        int y = (int)x;
        Console.WriteLine(y);
    }

in ILDasm:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  .maxstack  1
  .locals init ([0] int32 x)
  IL_0000:  nop
  IL_0001:  ldc.i4.5
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  box        [mscorlib]System.Int32
  IL_0009:  call       void Test.Program::Print(object)
  IL_000e:  nop
  IL_000f:  call       string [mscorlib]System.Console::ReadLine()
  IL_0014:  pop
  IL_0015:  ret
} // end of method Program::Main

.method private hidebysig static void  Print(object x) cil managed
{
  .maxstack  1
  .locals init ([0] int32 y)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  unbox.any  [mscorlib]System.Int32
  IL_0007:  stloc.0
  IL_0008:  ldloc.0
  IL_0009:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_000e:  nop
  IL_000f:  ret
} // end of method Program::Print

[mscorlib]System.Int32 is used for boxing/unboxing only. When the variable is in stack, it's always int32.


From Int32 and int:

It might be helpful to think of System.Int32 as a shadow type for int32.

The following C# code:

int x = 0;
x.ToString();

Is this in IL:

ldc.i4.0
stloc.0
ldloca.s 0
call instance class System.String [mscorlib]System.Int32::ToString()
pop

Notice how it's passing an int32 into a seemingly incompatible System.Int32 struct. The engine allows this cause it has been hardwired to recognise System.Int32 as the shadow type of int32.

like image 197
horgh Avatar answered Oct 13 '22 00:10

horgh