LA.NET [EN]

Jun 15

According to the C# futures site , C# 4.0 is all about dynamic programming. In practice, the idea is to ease the burden that the C# programmer must bear when interacting, for instance, with objects from dynamic languages or with COM objects (notice the docs mention other kind of objects, but these types should be enough for introducing the main ideas).

In an old blog entry, I’ve already covered two of the new features: named parameters and optional parameter. Today we’re going to pick up where we left and we’re going to start looking at Dynamic Lookup.

The docs say that “Dynamic Lookup allows you a unified approach to invoking things dynamically”. hum…not much explanatory, right? Ok, on this first post we’ll show dynamic lookup by concentrating on the work you need to when you need to use reflection for accessing a property/field or to invoke a method.

Let’s start by creating a new class that we’ll be using in this example:

class Student {
    public String Name { get; set; }
    private List<String> _classes = new List<String>();
    public void EnrollInClass(String className) {
        _classes.Add(className);
    }
    public IEnumerable<String> GetEnrolledClasses() {
        return _classes;
    }
}

Nothing special here…we’ve got a simple class with a property and two methods. Even though I’m no reflection guru, I’ve written one or two lines of reflection code in the past. For instance, the following code creates a new instance of a student, sets its name and enroll it in an English class:

var studentType = typeof(Student);
var studentInstance = Activator.CreateInstance(studentType);
studentType.GetProperty("Name")
                    .SetValue(studentInstance,"Luis",null);
studentType.GetMethod("EnrollInClass")
                     .Invoke(studentInstance, new[] { "English" });

As you can see, we need several steps:

  • we start by getting the type of the class (in this case, by using the typeof operator);
  • we create a new instance by invoking the static Activator’s CreateInstance method (notice that we could have used the generic method. I’ve opted for the non generic one because it’s the one you’ll find more often out there);
  • accessing a public property and setting a value means getting a reference to the property (the GetProperty method returns a PropertyInfo instance) and then calling the SetValue method over the object that represents the property;
  • Invoking a method is similar: you need to get a reference to the method (the GetMethod returns a MethodInfo instance) and then you call Invoke method over that instance (and eventually pass the required parameters).

Now, this ends being a little boring and a tidy bit hard when you have lot of reflection code to write. Wouldn’t it be great if I could simply write this:

dynamic std = Activator.CreateInstance(typeof(Student));
std.Name = "Luis";
std.EnrollInClass("English");

Yep, you can…at least in C# 4.0…the dynamic word identifies a dynamic type. Whenever the C# compiler encounters a dynamic type, it will “cancel” static checking. According to the docs, “when you have an object of type dynamic, you can “do things to it” that are resolved only at runtime”.

I’m still suspicious about the things you can do to the object :), but I’d say that in this example, the C# compiler ends up using reflection to access the passed information and checks the properties/methods you’re using.

Lets take a look at the code generated by the compiler. The firs thing you should notice after firing up reflector is that you can a new generated class which is used as container for the dynamic code:

[CompilerGenerated]
private static class <Main>o__SiteContainer0 {
    public static CallSite<Func<CallSite, object, string, object>> <>p__Site1;
    public static CallSite<Action<CallSite, object, string>> <>p__Site2;
    public static CallSite<Action<CallSite, Type, object>> <>p__Site3;
    public static CallSite<Func<CallSite, object, object>> <>p__Site4;
    public static CallSite<Action<CallSite, Type, object>> <>p__Site5;
    public static CallSite<Func<CallSite, object, object>> <>p__Site6;
}

Looking at Main, you should see several entries which look like this (I’ve tried formatting it for easy reading):

if (<Main>o__SiteContainer0.<>p__Site1 == null) {
   <Main>o__SiteContainer0.<>p__Site1 =
         CallSite<Func<CallSite, object, string, object>>.Create(
                new CSharpSetMemberBinder("Name",
                                     typeof(Program),
                                     new CSharpArgumentInfo[] {
                                         new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null),
                                         new CSharpArgumentInfo(
                                            CSharpArgumentInfoFlags.LiteralConstant  |               
                                            CSharpArgumentInfoFlags.UseCompileTimeType, null)
                                    })
              );
}
<Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, std, "Luis");

The previous snippet shows the code the compiler uses to set the Name property on the dynamic type. As you can see, it uses a CallSite type for encapsulating interaction with the dynamic type. When the CallSite hasn’t been initialized, the compiler ends up calling the state Create method for creating a new one. Notice that the CSharpSetMemberBinder type defines the type of binder you’ll be using for interacting with the member. If you use reflector for poking around the code, you should also see that it relies on the CSharpInvokeMemberBinder for setting up the method invocation. In fact, and since you have reflector opened, you should take a look at the Microsoft.CSharp.RuntimeBinder namespace. There you’ll find several binders which might end up being used for accessing the members of the “dynamic type”.

Interestingly, the Func parameter you pass when you create the CallSite ends up defining the the Target property of the CallSite (ie, it ends up defining the delegate that will be used for setting the property). Notice that you’re passing the container itself to the Target method call so that it may be updated with new info. As I’ve, setting the value is the responsibility of the binder (in this case, that task is executed by the CSharpSetMemberBinder itself, with the help of the internal BinderHelper class).

Final note of interest: using dynamic results in the creation of an object. Here’s the translation of the dynamic C# line we wrote:

object std = Activator.CreateInstance<Student>();

Well, I guess it’s enough for one day. There are lots of stuff that can be told about the internals, but the truth is that I didn’t paid a lot of attention to the DLR…On the other hand, I guess that it’s never late to start learning it :)

And that’s all for today…More on C# 4.0 in future posts.

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>