Click here to Skip to main content
15,881,802 members
Articles / Programming Languages / C# 4.0

C# 4.0: Dynamic’s compiler tricks

Rate me:
Please Sign up or sign in to vote.
4.25/5 (3 votes)
7 Jun 2009MIT3 min read 27.6K   15   6
So, I was curious today, I had some code that used dynamic and was wondering what the heck the compiler was doing on the back end. So, I wrote up a very small test program, and tore into it using Reflector. The version of Reflector I used still only decompiles to C# 3, so it [...]

So, I was curious today. I had some code that used dynamic and was wondering what the heck the compiler was doing on the back end. So, I wrote up a very small test program, and tore into it using Reflector. The version of Reflector I used still only decompiles to C# 3, so it happily displayed all of the inner workings to me without trying to hide any of it. Here is my test program:

C#
static void Main(string[] args)
{
    dynamic foo = "bar";
    int meep = foo.Length;
}

Nice and simple, it shouldn’t look too bad… Well, the compiler generates a static container class that it uses to keep track of some things, and it names everything using names that can’t possibly collide with the programmer’s code. So, instead of showing you code that could make your eyes bleed, I took the liberty of renaming everything and inserting new lines to help readability, the result was:

C#
static class CallSiteContainer
{
    public static CallSite<Func<CallSite, object, int>> convertSite { get; set; }
    public static CallSite<Func<CallSite, object, object>> getLengthSite { get; set; }
}

private static void Main(string[] args)
{
    object foo = "bar";

    if (CallSiteContainer.convertSite == null)
        CallSiteContainer.convertSite = CallSite<Func<CallSite, object, int>>
            .Create(new CSharpConvertBinder(typeof(int), 
		CSharpConversionKind.ImplicitConversion, false));

    if (CallSiteContainer.getLengthSite == null)
        CallSiteContainer.getLengthSite = CallSite<Func<CallSite, object, object>>
            .Create(new CSharpGetMemberBinder("Length", typeof(Program),
                new CSharpArgumentInfo[] {
                    new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null)
                }));

    int meep = CallSiteContainer.convertSite.Target.Invoke(
        CallSiteContainer.convertSite,
        CallSiteContainer.getLengthSite.Target.Invoke(
            CallSiteContainer.getLengthSite, foo));
}

So, the first thing to notice is that the compiler is generating a static inner container class that holds the call sites for the operation so that it doesn’t have to keep creating them over and over. A call site is a point where we interact with a dynamic object, we have two here, the get on .Length and the conversion of the result of that gets to an integer. Also notice that foo is actually just an object when things are all said and done, the compiler just wraps all interactions with it.

The generic parameter on the CallSite class ends up determining the signature of the call site’s Invoke method. Since the CallSiteContainer.convertSite ends up being the equivalent of the folling lambda, it takes Func<callsite> to say that it takes a CallSite, and the object to convert as parameters, then returns an integer.

C#
Func<object, int> convertSite = obj => (int)obj;

It just takes whatever object and tries to convert it to an int. The other call site is a bit trickier, because you cannot express it as a lambda at compile time, but you could generate one at runtime, look here for details on that. What we do know is that the CallSiteContainer.getLengthSite fetches the value of the Length property or field off of whatever object it is handed. It’s not yet clear to me why the CSharpGetMemberBinder takes the Program type and that CSharpArgumentInfo array, but it probably has something to do with how the DLR caches the code it generates. This whole thing is powered by the DLR, which does all the code generation and caching transparently on the back end. So be thankful for IronPython being around to kick all of this stuff off ;).

When we want to actually perform our operation, getting the Length of foo, and assigning it to meep, it just Invokes both of the call sites, passing the output of the getLengthSite to the convertSite, then taking the result of that and giving it to meep. I don’t know why each call site is passed itself as the first parameter, but after that comes what would be passed into the lambda equivalent of each call site. When the call site is Invoked, it is passed in so it can be updated to handle new cases that it hasn’t seen before, then all of the arguments that would have been passed into the lambda equivalent follow.

Well, that’s about it, all of this generated code isn’t too complex, it’s just pretty wordy. Looking at this and DynamicObject, it occurs to me that implementing Ruby style Mixins, methodMissing, and send shouldn’t be too hard. If you feel like I left anything out, please point it out in the comments.

EDIT: Incorporated information that Curt provided in the comments.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer
United States United States
I currently work as a Software Engineer for a company in North Carolina, mainly working with C#.

Comments and Discussions

 
GeneralNice demonstration on how not to write efficient code (not discrediting the article at all) Pin
Michael Lee Yohe9-Jun-09 10:15
Michael Lee Yohe9-Jun-09 10:15 
GeneralPlease Edit this article Pin
adatapost5-Jun-09 23:40
adatapost5-Jun-09 23:40 
GeneralRe: Please Edit this article Pin
StormySpike7-Jun-09 17:00
StormySpike7-Jun-09 17:00 
GeneralPost is a bit WIDE Pin
Robert Cooley5-Jun-09 18:57
Robert Cooley5-Jun-09 18:57 
RantRe: Post is a bit WIDE Pin
tonyt5-Jun-09 20:37
tonyt5-Jun-09 20:37 
GeneralRe: Post is a bit WIDE Pin
StormySpike7-Jun-09 17:01
StormySpike7-Jun-09 17:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.