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
It's rather hard to follow what's going on when reading the source of the core framework.
"how the value gets stored in m_value when we give int i=7;
"
Firstly, when the C# compiler sees int
, it just pretends you said System.Int32
instead (*). Similarly when it sees 7
, it says "aha! That's an integer literal. I'll store it in a System.Int32
". So then it creates the variable i
(of the right type), and initializes it with the value it created.
(*) That means that the source http://referencesource.microsoft.com/#mscorlib/system/int32.cs,225942ed7b7a3252 has:
public struct Int32 : *various bases*
{
internal Int32 m_value;
... which is ever so slightly confusing (and wouldn't normally be legal).
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.
That's just how the language works, man!
In the stateent
int i = 7;
You are creating a variable of Int32
. (i.e. the int i
part). However, you are assigning it with a 7
. A 7
in C# (and in many other languages) is called a literal. This means that 7
is already an instance of Int32
! It's like this:
Suppose you have a class
public class ClassA {
public int i;
}
And an instance of ClassA
:
ClassA obj = new ClassA ();
obj.i = 1;
And then you do this:
ClassA a = obj;
And you say, "I don't see any implicit operator in ClassA
so that the value could get stored in i
."
But obj
is already a legit object! Just like 7
.