Let’s take a quick look at the object keyword first. I’m not going to talk a lot about it because it’s been around since C# 1.0. This keyword is nothing more than a shortcut for System.Object, which is the root type in the C# class hierarchy. (However, as Eric Lippert pointed out in his blog post, not everything in C# derives from object.) This is a powerful mechanism, since you can assign almost any value to instances of this type.
Here is a short example that demonstrates some of the benefits and problems of using the object keyword.
object obj = 10; Console.WriteLine(obj.GetType()); // Prints System.Int32 because // this is the type of the value stored in this object.
// A compiler error, because // at compile time the type of obj is System.Object. // obj = obj + 10;
// You need to explicitly cast obj to a necessary type. obj = (int)obj + 10;
// However, this does not mean that you are really safe. // You can cast to a wrong type // and the compiler will not detect it. // Here you get an exception at run time, // because obj is an integer, not a string. // obj = (string)obj + 10;
// You also get a run-time exception // if you cast to a wrong numeric type, // even if there is an implicit conversion in the language. // obj = (double)obj + 10;
As you can see, although obj stores an integer, the compiler does not let you to perform any mathematical operations without a cast. It may look like casting helps you to ensure that you really have an integer, but it doesn’t. You can cast to a completely different type and the compiler will not detect it. As a result, you get a run-time exception.
So you have to perform an explicit cast that does not guarantee anything just because the compiler doesn’t let you to run your program without a cast.
Here is where the new dynamic keyword in C# 4.0 comes in. It tells the compiler not to enforce additional rules upon your code.
dynamic dyn = 10; Console.WriteLine(dyn.GetType()); // Same as “object”. // Prints System.Int32 because // this is the type of the value stored in this object.
// No compiler error, because // the compiler does not try to identify // the type of the dynamic object at compile time. dyn = dyn + 10;
// Also, this operation will succeed for all numeric // or other types that support a “+” operation. dyn = 10.0; dyn = dyn + 10;
dyn = “10”; dyn = dyn + 10;
This is one of the main differences between object and dynamic – with dynamic you tell the compiler that the type of an object can be known only at run time, and the compiler doesn’t try to interfere. As a result, you can write less code. And I want to emphasize that this is no more dangerous than using the original object keyword. However, it is not less dangerous either, so all the type-checking techniques that you need to use when operating with objects (such as reflection) have to be used for dynamic objects as well.
The next question that often comes up is something like the following: “Since a dynamic object can be anything and the compiler doesn’t check what it is, does it mean that you can pass a dynamic object to my unsuspecting method/system and make it crash?”
Let’s assume we have a simple method.
Now let’s look at how you can pass a dynamic object to it.
dynamic dyn = 10;
// You get an exception at run time here. Print(dyn);
As you can see, although the compiler allows you to pass a dynamic object to your method, your method never gets this object if it has a wrong type. An exception is thrown before the method is actually called. The only way you can pass a dynamic object to your method is if it contains a necessary value, in this case, a string.
Again, this is not that much different from the behavior you get with the object keyword.
object obj = 10;
// Doesn’t compile. //Print(obj);
// Compiles, but there is an exception at run time. //Print((string)obj);
// This code works because obj is now a string, // but you still need a cast. obj = “10”; Print((string)obj);
Some people say that it isn’t hard to read (int)obj, so why bother with dynamic? Well, there are situations when you have to perform so many cast operations that it makes your code almost unreadable. There are also situations when a simple cast is not enough and you have to call reflection methods, such as InvokeMember or GetProperties. A good example here is COM interop, which is why it was modified to use the new dynamic features (for more information, look at this “how-to”.)
Also, the dynamic keyword and dynamic language runtime enable many scenarios that were either impossible or difficult to do before, including interoperation with dynamic languages. I have highlighted couple of such scenarios earlier on this blog: Introducing ExpandoObject and Creating Wrappers with DynamicObject.
You may also want to take a look at the MSDN walkthrough that shows how you can call an IronPython library from C# and Visual Basic, and a really cool presentation, Using Dynamic Languages to Build Scriptable Applications, by Dino Viehland. Another good presentation that shows some examples and explains the design principles behind this feature is Dynamic Binding in C# 4 by Mads Torgersen.
The conclusion is that there is no need to fear that someone can break your code by using dynamic features. It is no more (and again, no less) dangerous than the object keyword.
So, if you often use the object keyword and have to perform a lot of casting and/or use reflection to call methods and properties of objects, you probably should take a look at the dynamic keyword. In some cases it’s more convenient than object and with less code to write.
0 comments