In my spare time I have been working on a Simple.Data provider for Sqlite. Simple.Data is a lightweight ORM written by Mark Rendle that leans heavily on dynamic types and metaprogramming techniques to create an api that is the closest to Ruby’s Active Record that I have ever seen in .NET. I have been using this project as a way to explore the new dynamic features introduced into C# and .NET with version 4. I have discovered some interesting things and wanted to share them.

Before we dive too deeply into dynamic and metaprogramming, let’s explore the evolution of typing in .NET and and it’s impact on how we write C#, the IDE and our wrists. Consider the following program.

using System;

namespace ExploringDynamic
{
    public class ASimpleType
    {
        string someProperty;
        public string SomeProperty
        {
            get { return someProperty; }
            set { someProperty = value; }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ASimpleType item = new ASimpleType();
            item.SomeProperty = "Explicitly Typed";
            Console.WriteLine(item.SomeProperty);
            Console.ReadKey();
        }
    }
}

This simple program represents the prototypical .NET application and is the reason I have carpel tunnel syndrome. It will easily run under any version of the .NET framework and will compile using any version of the C# compiler. We start with a simple class definition that defines the type ASimpleType. The class has a field and a getter and setter for that field exposed as the property SomeProperty. The program creates an instance of the type, sets the property SomeProperty’s value to “Explicitly Typed” and then writes the value to the console using the property. This form of C# is very explicit and verbose and is the form we as .NET developers are most familiar.

using System;

namespace ExploringDynamic
{
    public class ASimpleType
    {
        public string SomeProperty { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var item = new ASimpleType { SomeProperty = "Implicitly Typed" };
            Console.WriteLine(item.SomeProperty);
            Console.ReadKey();
        }
    }
}

With .NET 3.5 came the introduction of the .NET 2.0 runtime and compilers for C#. The compiler added several new language features demonstrated above. The type has been greatly simplified by using Auto Properties. Object creation has changed with the introduction of the var keyword and the use of object initializer syntax. Even though we have introduced several new language features into the program, we can compile this application targeting the .NET 1.1 runtime and it will be functionally equivalent to the original program. These features are for the most part syntactical sugar provided to us by the compiler which is making implicit typing decisions for us.

Update: Looks like I got my history a bit mixed up. Take a look at the insightful comments for further details. With that said the evolution of the language features described here still holds true. It is the versions of the runtimes and frameworks, I got a bit wrong.

This concept can be pushed further with .NET 3.5.

using System;

namespace ExploringDynamic
{
    class Program
    {
        static void Main(string[] args)
        {
            var item = new { SomeProperty = "Anonymously Typed" };
            Console.WriteLine(item.SomeProperty);
            Console.ReadKey();
        }
    }
}

We have now completely removed the need for the class definition by using Anonymous typing. At compile time the compiler will examine this code and generate a type for us to represent the anonymous type. This program can still be compiled targeting the .NET 1.1 runtime and is functionally equivalent to the original program. Anonymous typing does have its limitations.

using System;

namespace ExploringDynamic
{
    class Program
    {
        static void Main(string[] args)
        {
            var item = GetType();
            Console.WriteLine(item.SomeProperty);
            Console.ReadKey();
        }

        static object GetType()
        {
            return new { SomeProperty = "Anonymously Typed" };
        }
    }
}

This program will not even compile. The properties implicitly defined on our anonymous object are only available for use within the scope of the definition of the anonymous object. We can return the anonymous object from the GetType method only as object, so the implicitly typed item variable only has the properties and methods of object available. We will get a compile time error related to the use of the SomeProperty property with any version of the C# compiler.

With the introduction of the dynamic features of .NET 4, can get around this limitation.

using System;

namespace ExploringDynamic
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic item = GetType();
            Console.WriteLine(item.SomeProperty);
            Console.ReadKey();
        }

        static dynamic GetType()
        {
            return new { SomeProperty = "Anonymously Typed" };
        }
    }
}

In this sample, we have simply changed the return type of the GetType method to dynamic. This adds a dependency on the .NET 4 runtime and compilers. The program will compile and run as expected, but we have fundamentally changed the program.

In the previous, versions the compiler was running several type based checks on our code to ensure type safety. The compiler would throw an error if it could not match a property or method to the type we declared a variable. With this version the dynamic keyword is explicitly telling the compiler to not bother with those checks. We could modify line 10 to look like the following code and the compiler would happily accept it.

Console.WriteLine(item.ZippityDoDah);

This causes the program to crash at runtime with a RuntimeBinderException. This exception gives us some insight into another fundamental change introduced into the program. By using the dynamic keyword we have told the compiler to include the use of theDynamic Language Runtime in the executable.

When the program runs and the runtime encounters a dynamic variable, the DLR is invoked to attempt to resolve the the members of the variable. In the program, the type that is returned by by the GetType method is still a static type, it’s an anonymous type but a type none the less.

Consider the following modification to the program.

using System;

namespace ExploringDynamic
{
    class Program
    {
        public class ASimpleType
        {
            public virtual string SomeProperty { get; set; }
        }

        static void Main(string[] args)
        {
            var item = GetType();
            var item2 = (ASimpleType) item;
            Console.WriteLine(item2.SomeProperty);
            Console.ReadKey();
        }

        static dynamic GetType()
        {
            return new ASimpleType { SomeProperty = "Explictily Typed" };
        }
    }
}

In this example, we have returned to the explicit ASimpleType type and are returning it from our GetType method as a dynamic reference. We can simply cast the reference to the explicit type and carry on as normal. So the dynamic keyword word simply allows the compiler to ignore types at compile time, but dynamic variables can easily reference static types.

One language feature introduced in .NET 3.5 that has not been mentioned yet is extension methods.

using System;

namespace ExploringDynamic
{
    public class ASimpleType
    {
        public virtual string SomeProperty { get; set; }
    }

    public static class Extensions
    {
        public static string GetProperty(this ASimpleType item)
        {
            return item.SomeProperty;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var item = GetType();

            Console.WriteLine(item.GetProperty());
            Console.ReadKey();
        }

        static dynamic GetType()
        {
            return new ASimpleType { SomeProperty = "Explictily Typed" };
        }
    }
}

Based on what we have covered so far, what do you think should happen here? Surprisingly, when we run this version of the program we will get a RuntimeBinderException again. Extension methods are a compiler trick, to make it look like a static type has the extension method in the IDE. Once it is compiled, it is actually a static method call. I can make this program work at runtime by simply casting the return value of the GetType method to the explicit type.

 var item = (ASimpleType) GetType();

Up to this point we have simply played with the dynamic keyword in relation to statically typed objects. The dynamic keyword was introduced to allow us to interact with truly dynamic types. But what is a dynamic type and how do I create one?

A common gateway to dynamic objects is theExpandoObject class.

using System;
using System.Dynamic;

namespace ExploringDynamic
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic item = new ExpandoObject();
            item.SomeProperty = "Dynamic Object";
            item.GetProperty = (Func<string>) (() => item.SomeProperty);

            Console.WriteLine(item.SomeProperty);
            Console.WriteLine(item.GetProperty());

            item.GetProperty = "BOOOOOOM! Mind BLOWN!";

            Console.WriteLine(item.GetProperty);
            Console.ReadKey();
        }
    }
}

Let that sink in for a second. Look closely at what appears to be going on there. Feel free to visit the MSDN documentation for ExpandoObject and look for the definition of SomeProperty or GetProperty. Notice that I am defining GetProperty as a Func and calling it like a method in one section of code then reassigning it a string value and using it like a property in another section of code.

This is a dynamic object. There is no compiler baby sitting the source code to ensure type integrity. We are free to redefine anything we want on the object. Some people call dynamic typing duck typing, as in “If it walks like a duck and quacks like a duck it must be a duck.”

The ExpandoObject provides canned functionality for mapping members and functions dynamically. But we can implement our own dynamic objects with custom functionality.

using System;
using System.Dynamic;

namespace ExploringDynamic
{
    class Program
    {
        public class ConsoleTalker : DynamicObject
        {
            public override bool TryInvokeMember(InvokeMemberBinder binder,
                                                 object[] args,
                                                 out object result)
            {
                var name = binder.Name;

                if(name.StartsWith("Say"))
                    Console.WriteLine(name.Substring(3) + "!");
                else
                    Console.WriteLine("I don't understand.");

                result = true;
                return true;
            }

            public void SayWorld()
            {
                Console.WriteLine("Universe! Woot.");
            }
        }

        static void Main(string[] args)
        {
            dynamic item = new ConsoleTalker();

            item.SayHi();
            item.SayWorld();
            item.WhisperBoo();

            Console.ReadKey();
        }
    }
}

This introduces metaprogramming into the simple application. The ConsoleTalker class overrides the TryInvokeMember method of DynamicObject. When I invoke a method on an instance of this object, the DLR will attempt to execute the method directly, so calls to the SayWorld method prints “Universe! Woot.” to the console.

If the method is not found, it will call the TryInvokeMember method in an attempt to handle the unknown call. This is very similar to Ruby’s method missing functionality. You can checkout the rest of the API for DynamicObject here.

It is worth nothing here that if I reference the ConsoleTalker explicitly, the compiler kicks in at compile time and will complain about dynamic method and property use. So, while the following is valid it disables dynamic method resolution for all intents and purposes.

var item = new ConsoleTalker();

Even if we use the var keyword, the compiler will assume static typing. We must explicitly use the dynamic keyword.

So what can be accomplished with dynamic and metaprogramming techniques? Consider this program that connects to a Northwind database and prints the name and phone number of all German customers to the console.

public User FindUserByEmail(string email)
using System;
using System.IO;
using System.Reflection;
using Simple.Data;

namespace ExploringDynamic
{
    class Program
    {
        private static readonly string DatabasePath =
            Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),"Northwind.db");

        static void Main(string[] args)
        {
            var db = Database.OpenFile(DatabasePath);
            foreach(var customer in db.Customers.FindAll(db.Customers.Country == "Germany"))
            {
                Console.WriteLine("Customer: {0} Phone: {1}", customer.ContactName, customer.Phone);
            }

            Console.ReadKey();
        }
    }
}


blog comments powered by Disqus

Published

28 March 2011

Tags