I think I've known the answer for a class, just want to confirm my understanding is correct. Let's say I have a ClassA
and its instance named a
. When a.MethodA()
is invoked:
(1) CLR find the type of ClassA
by the type pointer of a
in the heap(the type have been loaded into the heap)
(2) Find the MethodA
in the type, if not found, go to its base type, until the object
class.
Maybe my understanding is not quite precise, but I think it's basicly correct(Correct me if it's wrong!). And here comes the question of a simple struct.
struct MyStruct
{
public void MethodA() { }
}
I have var x = new MyStruct();
, its value is on the stack, and the type of MyStruct
has been loaded into the heap. When execute x.MethodA()
, of course no boxing. How CLR find MethodA
and get the IL and execute/JIT it? I think the answer is probably:(again, correct me if I'm wrong)
(1) we have the declaring type of x
on the stack. CLR find its type by the info on the stack, and find MethodA
in its type. -- let's call it assumptionA
.
I'll be happy if you tell me my assumptionA
is correct. But even it's wrong, it tells a truth: CLR has a way to find a struct's type without boxing.
Now what about x.ToString()
or x.GetType()
? We know that the value will be boxed, and then it will perform like a class. But why do we need boxing here? Since we can get its type(assumptionA tells us), why not go to its base type and find the method(just like a class)? Why need an expensive box operations here?
AssumptionA is wrong. The C# compiler's symbol table stores type information. That static type information is used in nearly all cases, the dynamic type stored in an object is only needed during type checks (is
operator), casting (as
operator and actual cast syntax), and array variance, and then only when the dynamic type isn't known to the compiler. The dynamic type of an unboxed struct is always statically known, and dynamic type of a class instance is statically known near the instantiation and inside a conditional block which performed a type check (e.g. in if (x is T) y = (T)x;
the type is known inside the then-part, so the cast doesn't require another dynamic check).
Ok, now because the C# compiler statically knows the type of x
, it can do overload resolution and find the exact MethodA being called. Then it emits MSIL to push the arguments onto the MSIL virtual stack and issues a call instruction containing a metadata reference to that particular method. No type checks are needed at runtime.
For x.ToString()
, the C# compiler still knows the exact method it wants to call. If ToString
has been overridden by the struct
type, it expects a parameter of type pointer-to-MyStruct
, which the compiler handles without boxing. If ToString
has not been overridden, the compiler generates a call to Object.ToString
, which expects an object as its parameter. To push x
on the MSIL virtual stack as the correct type requires boxing.
GetType
is a special case when the type is known statically, the compiler won't call any method, it just gets the type information from the symbol table and stuffs the metadata reference into the MSIL directly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With