Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

The Dynamic Keyword in C# 4.0

0.00/5 (No votes)
6 Apr 2010 1  
Introduction to the 'dynamic' keyword.

Introduction

This article is the 2nd in a series, introducing some of the new language features of C# 4.0. My first article covered Named and Optional Arguments[^]. It is worth re-iterating that this series is intended as a bite-sized introduction to the language features, not an in-depth analysis, aimed at beginners to C# 4.0 in particular and C# generally.

As discussed in the first article, one of the primary aims in C# 4.0 is to increase support for dynamic languages which are making their way into the framework; naturally, the dynamic keyword plays an important part. I do not intend to discuss the DLR, as this is largely invisible except when dealing with dynamic languages, which I have little experience of.

What is the dynamic type for?

dynamic is a new static type that acts like a placeholder for a type not known until runtime. Once the dynamic object is declared, it is possible to call operations, get and set properties on it, even pass the dynamic instance pretty much as if it were any normal type. This looks like fairly standard code:

//This could be the result of call to Python / Ruby etc !
dynamic myDynamic = GetDynamic(....);
myDynamic.Foo("Hello"); //Call a method
myDynamic.Bar = myDynamic.Baz; //Using properties
int Quux = myDynamic[0]; //calling on an indexer
int Qux = myDynamic(12,5); //Invoking as delegate
MyMethod(myDynamic); // Passing as parameter

Dynamic support will probably be the most controversial new feature of C# 4.0. I have heard fellow developers complain that it undermines the OO nature of C# as it provides a way of defining an "Unknown" type at compile time and performing "Unknown" operations on it. I admit that until I started to play with the features, I was in agreement with this. So why the fuss? The type is unknown until runtime (depending on what is being interop'd with, the type might have been generated on the fly!), making it impossible for the CLR compiler to know if the operations on the dynamic object exist (this is in contrast with the var keyword).

For example, if the dynamically created object does not have a method Foo taking a string, the above code will compile, but it will throw an RuntimeBinderException if executed. Herein lies the problem. We now have a mechanism that bypasses the normal strict typing of C#. Code can now be compiled that would normally not if thoroughly checked at compile time. In addition, dynamics, if misused, can make refactoring more difficult. E.g., if the object that gets created in the GetDynamic(....) call is refactored to rename its Foo(string s) method, the code in my snippet won't be changed automatically; in fact, it will still build.

Why use dynamic?

In the statically typed world, dynamic gives developers a lot of rope to hang themselves with. When dealing with objects whose types can be known at compile time, you should avoid the dynamic keyword at all costs. Earlier, I said that my initial reaction was negative, so what changed my mind? To quote Margret Attwood, context is all. When statically typing, dynamic doesn't make a stitch of sense. If you are dealing with an unknown or dynamic type, it is often necessary to communicate with it through Reflection. Reflective code is not easy to read, and has all the pitfalls of the dynamic type above. In this context, dynamic makes a lot of sense.

Underneath the hood, the dynamic type compiles down to wrapped reflective code, unless the object is a COM object (in which case, operations are called dynamically through IDispatch) or the object implements IDynamicObject, which is used extensively by IronPython and IronRuby and also in up-an-coming APIs. IDynamicObject subclasses are asked to perform the operation directly, allowing the dynamic operations to be re-defined. Seen this way, dynamic is an excellent addition to the C# arsenal. The section "A Worked Example" provides a re-working of reflective code as a demonstration. But first, let us look at the more general properties of dynamic objects.

Assignment and declaration

The object can be assigned directly (and implicitly converted):

dynamic d = "Hello World"; //Implicit conversion
string s = d; //Conversion at Assignment

As described above, a dynamic object can be declared as the result of some method:

dynamic d = GetDynamicObject(...);

The following will not compile, the use of an unassigned variable message will be reported:

dynamic d;
dynamic type = d.GetType();

Common RuntimeBindingExceptions

Now is a good time to look at the RuntimeBindingExceptions. In general. these occur when the operation called cannot be resolved at runtime. The following will build, but would throw a RuntimeBindingExceptions described in the comments, if executed:

//Attempting to access null reference
dynamic nulled = null;
dynamic type = nulled.GetType();
//Cannot perform runtime binding on null reference.
//Accessing a non-existant property
dynamic foo = 7;
dynamic baz = myInt.Bar; //'int' does not contain a definition for 'Bar'
//Accessing a non-existant method
dynamic foo = 7;
dynamic baz = myInt.Baz(); //'int' does not contain a definition for 'Baz'
//Bad implicit conversion
dynamic foo = "Hello";
int bar = foo; // Cannot implicitly convert type 'string' to 'int'.

The statically typed version of the above would not compile, and so the dynamic code is weaker if the type is known at compile time. But if compared to, for example reflective calls, the exception mechanism is neater and more transparent to the developer.

Overload resolution

Foo foo = new Foo();
dynamic myDynamic = new Bar();
var returnedValue = foo.Baz(myDynamic);

The runtime will resolve the overloads of Baz in Foo based on the runtime type of myDynamic. Note that no overload resolution between a dynamic and non-dynamic parameter is needed, as the following is invalid:

public class Foo
{
   public void Bar(AnyType baz)
   {
   }
   
   //Won't compile as conflicts with first overload
   public void Bar(dynamic baz)
   {
   }
}

Limitations of dynamic

  • Extension methods cannot be called
  • Anonymous functions (lambdas) cannot be called
  • Because of the above, LINQ support is limited; check out on MSDN the Extension Methods of List<T> alone; all of these are unavailable on a dynamic instance

A worked example

My worked example (available as the solution accompanying this article) shows the use of dynamic to improve a code base where reflectively called operations (a method call and a get accessor) on an object are replaced by equivalent dynamic-based code.

The test rig

My solution has a very simple object model. There are two classes, Class1 and Class2, that implement the following:

public interface IBaseClass
{
    string Property { get; set; }
    void Method();
}

In the implemented classes, calling Method writes "Method Called on ClassName" to console, whereas accessing the getter of Property returns "Property of ClassName".

In the solution, there is an InstanceCreator class that returns instantiated objects of the type IBaseClass by calling the relevant constructors. This is to support one set of tests which uses direct operation calls to POCOs, and this requires concrete instances of a known type. The objects returned by InstanceCreator can be considered to be from a COM or dynamic source for the dynamic tests (with the caveat that they don't implement the respective IDispatch or IDynamicObject interfaces).

Finally, there are three tester classes representing three types of execution. StaticTester calls the object in a statically typed POCO way, RelectiveTester simulates a call to the object as if it were reflectively created at runtime with an unknown type, and DynamicTester replaces the reflective code with cleaner dynamic code. Note that, in the source, instantiation could have been performed reflectively for the reflective and dynamic examples as they are type-agnostic.

Calling statically

No surprises:

public class StaticTester : Tester
{
    void WriteClassDetails(IBaseClass instance)
    {
        instance.Method();
        Console.WriteLine(instance.Property);
    }
}

The method and property get called directly. Note that it is impossible to call a non-existent property or method like this as the code will not compile because the type is known to the CLR compiler.

Calling by Reflection

Now, let us pretend we need to call the operations reflectively, as the object type is unknown until run-time. Due to the properties and methods being called via their names (expressed as strings), it is possible to attempt to call a non-existent operation. Code has been added to protect against this (in italics):

public class ReflectiveTester : Tester
{

    static void WritePropertyReflectively(object instance, string propertyName)
    {
        Type type = instance.GetType();
        PropertyInfo propertyInfo = type.GetProperty(propertyName);
        if (propertyInfo == null)
            Console.WriteLine("Property \"{0}\" not found, propertyInfo " + 
                              "is null\r\npropertyInfo.GetValue(...) will result " + 
                              "in a NullReferenceException", propertyName);
        else
            Console.WriteLine(propertyInfo.GetValue(instance, null));
    }

    static void CallMethodReflectively(object instance, string methodName)
    {
        Type type = instance.GetType();
        MethodInfo methodInfo = type.GetMethod(methodName);
        if (methodInfo == null)
            Console.WriteLine("Method \"{0}\" not found, " + 
                              "methodInfo set to null\r\nmethodInfo.Invoke(...) " + 
                              "will result in a NullReferenceException", methodName);
        else
            methodInfo.Invoke(instance, null);
    }

    static void WriteClassDetails(object instance)
    {
        Type type = instance.GetType();
        WritePropertyReflectively(instance, "Property");
        CallMethodReflectively(instance, "Method");
    }
}

Note that it is now far less obvious the code is behaving as if the reflective code "hides" it. It is also a common mistake for developers to forget to protect against the "found" PropertyInfo or MethodInfo being null. Further execution in such cases would result in a NullReferenceException. This can be misleading to the unwary as often parameters are passed which could also raise this error type, though in my example, these are null. Furthermore, my example tidies away the defensive code slightly, often an error will need to be thrown and caught.

Using dynamic

Now, we will use dynamic, achieving the same thing as the reflective code. Again, the object type is not known until run-time, but the code is much closer to our first example. It is obvious what is intended to be called as we can call the methods and properties as if they are known at runtime. However, we still must defensively code as we are not certain the operations on the passed instance exist. Luckily, we need only worry about the RuntimeBinderException, making the code cleaner.

public class DynamicTester : Tester
{
    void WriteClassDetails(dynamic instance)
    {
        try
        {
           Console.WriteLine(instance.Property);
           instance.Method();
        }
        catch (RuntimeBinderException ex)
        {
            ErrorWriters.WriteRuntimeBinderException(ex);
        }
    }
}

One thing to conclude on is that this is not less "OO" than the reflective call; it is directly equivalent, but it has a neat syntax and less obtuse exception mechanism. If viewed this way, the addition of dynamicis a large benefit, even before the dynamic language support is considered.

Conclusion

Dynamics are a powerful new tool that make interop with dynamic languages as well as COM easier, and can be used to replace much turgid reflective code. They can be used to tell the compiler to execute operations on an object, the checking of which is deferred to runtime.

The great danger lies in the use of dynamic objects in inappropriate contexts, such as in statically typed systems, or worse, in place of an interface/base class in a properly typed system. 

A note about the solution

The solution provides the source code for the examples provided here. It was created with VS2010 RC1; naturally, you must have C# 4.0 for it to compile. The solution is a simple console application that runs through the tests for the three main variations of the code: calling methods on Plain Old C objects, calling via Reflection, and using dynamics. A method in Program.cs has several commented examples of bad and uncompilable code that can be uncommented and executed to see their effects. See the file for details.

History

  • 29 March, 2010: Article created.
  • 31 March, 2010: Fixed error in the article title.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here