More late-bound invocation scenario notes
There are various other scenario’s in the invocation space that have not been dissected. I mentioned a few of them in the comments section of one of my other posts. To be a little more illustrative, this posting will iterate over some of these scenario’s, and provide notes for each. It’s worthwhile to remember, this is simply a braindump, and until we’ve firmed up the story for Whidbey, I wouldn’t recommend you take this as “written in stone”.
Some late bound invocation scenario’s may include:
• Known method signature (parameters), unknown method name and method type
• Known signature and known method name, unknown receiver
• Known name
I’ll be talking about the first two for these particular notes.
1. Known method signature (parameters), unknown method name and method type
This scenario is presented by an extensibility mechanism where the user specifies a particular custom attribute on a method with the given signature. At runtime, the methods with the custom attribute are retrieved and thus the callback is available. In this scenario, the arguments are typically provided by the code invoking the callback (the source) so there is usually no coercion/conversion that needs to happen before hand. There is generally nothing unknown at the point of invocation.
Invocation through reflection is very heavy for such scenario’s, and a delegate is the right solution. Reflection invocation is heavy because there is a single entry point, so it’s forced to do heavy lifting, like hit metadata to understand the method, signature walking, argument checking etc. If the signature is known, a delegate is definitely the right choice.
The delegate story has an interesting spin to it. When you create a classic delegate over an instance, it is tied one-to-one to the instance of that type. If the delegate is over a static function you don’t need to worry about the instance, so the type of the method does not come in to the picture – it’s a simple question of scoping. If the delegate is over an instance, then you have the issue of the instance possibly changing.
Note:
It’s worthwhile explaining the last sentence a little further. Consider the following: Class A { void M() } and a delegate void del(). When I do a Delegate.CreateDelegate() over the instance of A.M, and my instance of A changes to another instance of A, I need to recreate the delegate.
So consider the scenario where the instance is not always stable, we have introduced the recreation of the delegate into the picture. So now it’s a tradeoff between recreating the delegate and invoking, or using a MethodInfo.Invoke. We can either create the delegate multiple times for each instance, or we can cook up a MethodInfo, and utilize the MethodInfo’s independent binding to the instance (via the API – specifying the receiver object).
We have to consider the performance aspects here: creation of the delegate is pretty much the same as the binding of the MethodInfo, and Delegate.Invoke is much faster than MethodInfo.Invoke. The ratio we need to consider to produce a working result, is the ration of creation to invocation. If the ratio is one to one, the MethodInfo.Invoke is probably a better solution. If the ratio of invocation over creation is high, than delegate makes sense. If the ratio is different than these options, the user of the API should go away and think about what make sense (ie: benchmark and find out). There is no real concrete story here, as always, we recommend that the user measure their scenario.
2. Known signature and known method name, unknown receiver
This scenario is very similar to the previous model, except that the method is identified by name and not via a custom attribute. Besides the startup logic (actually binding to the method), the considerations for the previous case are very similar.
An alternative to these cases in a fully trusted application, is the use of the IL instruction “calli”. The obvious drawback of calli is the unverifiable nature of the instruction. Because of this, erroneous usage of calli may lead to type safety and security holes. However, if used correctly, calli offers a great benefit in both speed and working set performance.
One obvious advantage of calli over delegates is that it binds to a signature and can take different types of instances. The usage of delegates requires that the delegate must be recreated when the instance changes. Calli uses a function pointer (IntPtr value) that can be retrieved at startup (Give me the MethodInfo, then the MethodHandle and then call GetFunctionPointer), and is extremely light.
Note:
Obviously, you can’t generate a calli call in C#. The way out of this if you’re using C#, you can generate an assembly (Reflection.Emit, ILASM etc) that has a method that takes an IntPtr, and performs a calli. One other thing to note, C++ uses calli extensively for virtuals and invocation.
Trusted components (particularly system components) that have tight performance requirements should pay great consideration to this late bound invocation solution.
Comments
- Anonymous
May 03, 2004
Joel,
I asked in few other places, but never got any answers.
Why can't CLR team add ability to set Target on a delegate?
This would allow for efficient implementation of known method / unknown object.
Thanks - Anonymous
May 09, 2004
The comment has been removed - Anonymous
July 19, 2004
Hi Joel,
I'm currently developing a "message reifier" for MSIL. The main idea is to turn "message passing" (method calls - field accessing - etc) into first-class citizens (ie objects). I'm using RAIL to analyze a compiled assembly and I'm replacing every call/callvirt/calli with a couple of MSIL instructions that redirect flow to a "reifier" object, who should afterwards let the original flow keep its original way.
What do you think would be the best alternative for this scenario? I'm currently passing both the assembly & method name, doing a lookup of the method and using Invoke() to call it. Do you think this could be achieved using delegates?
TIA,
Alan - Anonymous
August 03, 2004
How to invoke methods with an "out" parameter?
A MethodInfo of a static method with an out parameter had a parameter type as "GeneralTDS.MyDataTable&".
What does the "&" mean?
I'll appreciate if you can reply to true_9090 @ yahoo .com
Thanks. - Anonymous
January 29, 2006
Very,
Very interesting interview by Anders Hejlsberg (the lead C#
architect) with Bruce Eckel...