Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How CLR works when invoking a method of a struct

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?

like image 464
Cheng Chen Avatar asked Mar 31 '11 02:03

Cheng Chen


1 Answers

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.

like image 114
Ben Voigt Avatar answered Nov 10 '22 04:11

Ben Voigt