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:
dynamic myDynamic = GetDynamic(....);
myDynamic.Foo("Hello"); myDynamic.Bar = myDynamic.Baz; int Quux = myDynamic[0]; int Qux = myDynamic(12,5); MyMethod(myDynamic);
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"; string s = d;
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 RuntimeBindingException
s. In general. these occur when the operation called cannot be resolved at runtime. The following will build, but would throw a RuntimeBindingException
s described in the comments, if executed:
dynamic nulled = null;
dynamic type = nulled.GetType();
dynamic foo = 7;
dynamic baz = myInt.Bar;
dynamic foo = 7;
dynamic baz = myInt.Baz();
dynamic foo = "Hello";
int bar = foo;
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)
{
}
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
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 dynamic
is 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.