[This is a very old article I wrote back in 2002 when I worked for a company which built MRI scanners and was subsequently bought by Oxford Instruments. With COM being once again relevant with the introduction of WinRT, I thought it might be useful to revisit some core COM and .NET concepts.]
Every object in the CLR has type information associated with it, in the form of a 36-byte header:
Every method on the object has an entry in the method table (which acts in a similar way to a vtable in C++). Each entry is in effect a function pointer, and all method calls on the object come via the method table.
Immediately following object construction (using the .ctor or .cctor method) the objects method table appears thus:
The entry in the object’s method table points to the first element of the Pre-stub worker (the Instruction Pointer). This in turn has been initialised to point to an execution block in the JIT compiler. The JIT compiler then dereferences the second element (the IL Pointer) of the Pre-stub worker to read the IL code for the requested method from the Assembly where the method resides.
The JIT compiler then JITs the IL code to produce native machine code (e.g. x86 instructions), and then redirects the first member of the Pre-worker stub to point to that native code instead:
Following JITting, the method pointer has only a single indirection in the stub before reaching native code. This is actually better than native Win32 DLLs, where, due to base address relocation, there is usually a second indirection.
Interface Method Calls
There is no obvious order for interface method implementations in the method table; unlike the situation for classes, a derived object can implement the methods of a ‘higher’ interface than its ancestor. Therefore, an extra level of indirection is needed to map the method table entries of interface methods to their implementations:
Dynamic Code Execution
How to execute code based on user input, e.g. “Execute the method called ‘FooBar’” is solved in a much more efficient way than under COM, where the IDispatch interface with Invoke was notoriously slow.
Under .NET there are three basic options:
1. Interface and case statement
Devise an interface (e.g. IDynamicExecute) and have the implementing class instance check in a case statement the requested method name against the list of methods it implements.
Disadvantage: no compile-time type checking.
2. GetMethod, Invoke
Use GetType.GetMethod(<methodname>) and <method>.Invoke. Use the MethodInfo type to return a list of methods by signature (but not return value).
Disadvantage: about 1000× slower than standard method calls, due to run-time type-checking.
To grab a function pointer, Method.MethodHandle.GetFunctionPointer can be used, but IL assembly code is required to make use of this.
Use Delegate.CreateDelegate to create a method variable dynamically bound to a specific object instance. Delegate methods are marked with the runtime modifier to show that their implementations will be created on-the-fly by the CLR when the delegate object is created. The code they execute is specified at runtime.
Execution overhead is small, with only eight extra machine instructions; the ecx register (this pointer) is flipped by the CLR to point to the code to execute.
Asynchronous method calls use Delegates. Events allow [un]registration for specified conditions, and are also implemented using Delegates. Threads from the CLR thread pool are used to fulfil the async request.
Rules developed for async method calls in COM+ and MTS apply here.
This document is based on a lecture given at DevWeek 2002.
- vtable: virtual method table
- .ctor: constructor method
- .cctor: class (static) constructor method