About the book
This is a sample chapter
of the book C# in Depth, Second Edition.
It has been
published with the exclusive
permission of Manning.
Get 30% discount
DotNetSlacker readers can get 30% off the full print book or ebook at www.manning.com using the promo code
dns30 at checkout.
C# has always been a statically typed language, with no exceptions. There have been a few areas where the compiler has looked for particular names rather than interfaces, such as finding appropriate Add methods for collection initializers, but there's been nothing truly dynamic in the language beyond normal polymorphism. That changes with C# 4 - at least partially. The simplest way of explaining it is that there's a new static type called dynamic, which you can try to do pretty much anything with at compile time, and let the framework sort it out at execution time. Of course there's rather more to it than that, but that's the executive summary.
Given that C# is still a statically typed language everywhere that you're not using dynamic, I don't expect fans of programming to suddenly become dynamic C# advocates. That's not the point of the feature: it's all about interoperability. As dynamic languages such as IronRuby and IronPython join the .NET ecosystem, it would be crazy not to be able to call into C# code from IronPython and vice versa. Likewise developing against weakly-typed COM APIs has always been awkward in C#, with an abundance of casts cluttering the code. Dynamic typing addresses all of these concerns.
One word of warning though - it's worth being careful with dynamic typing. It's certainly fun to explore, and it's been very well implemented, but I still recommend that you think carefully before using it. Just like any other new feature, weigh up the pros and cons rather than rushing into it just because it's neat (which it undoubtedly is). The framework does a fine job of optimising dynamic code, but it will be slower than static code in most cases. More importantly, you lose a lot of compile-time safety; while unit testing will help you find a lot of the mistakes that can crop up when the compiler isn't able to help you much, I still prefer the immediate feedback of the compiler telling me if I'm trying to use a method which doesn't exist or can't be called with a given set of arguments.
Dynamic behavior can be very useful in situations where you're naturally dealing with dynamic environments or data, but if you're really looking to write large chunks of your code dynamically, I suggest you use a language where that's the normal style instead of the exception. C# is still very obviously a language which was designed for static typing: languages which have been dynamic from the start often have various features which will help you work more productively with dynamic behavior. Now that you can easily call into such languages from C#, you can fairly easily separate out the parts of your code which benefit from a largely dynamic style from those where static typing works better.
I don't want to put too much of a damper on things though: where dynamic typing is useful, it can be a lot simpler than the alternatives. In this article we'll take a look at the basic rules of dynamic typing in C# 4, and then in the second part we'll dive into some examples: using COM dynamically, and calling into some IronPython code.
What? When? Why? How?
Before we get to any code showing off this new feature of C# 4, it's worth getting a better handle on why it was introduced in the first place. I don't know any other languages which have gone from being purely static to partially dynamic; this is a significant step in C#'s evolution, whether you make use of it often or only occasionally.
We'll start off by taking a fresh look at what "dynamic" and "static" mean, consider some of the major use cases for dynamic typing in C#, and then delve into how it's implemented in C# 4.
What is dynamic typing?
In a statically typed language, the compiler knows the type of expressions in the code, and knows the members available on any type. It applies a fairly complex set of rules to determine which exact member should be used. This includes overload resolution; the only choice which is left until later is to pick the implementation of virtual methods depending on the execution-time type of the object. The process of working out which member to use is called binding, and in a statically typed binding language it occurs at compile time.
In a dynamically typed language, all of this binding occurs at execution time. A compiler is able to check that the code is syntactically correct, but it can't check that the methods you call and the properties you access are actually present. It's a bit like a word processor with no dictionary: it may be able to check your punctuation, but not your spelling. (If you're to have any sort of confidence in your code, you really need a good set of unit tests.) Some dynamic languages are always interpreted, with no compiler involved at all. Others provide both an interpreter and a compiler, to allow rapid development with a REPL: a read-evaluate-print loop.
Strictly speaking, REPL isn't solely associated with dynamic languages. Some statically typed languages have "interpreters" too which actually compile on the fly. Notably, F# comes with a tool called F# Interactive which does exactly this. However, interpreters are much more common for dynamic languages than static ones.
eval function, for example. To execute code based on data in strings, you need to use either the CodeDOM API (and CSharpCodeProvider in particular) or simple reflection to invoke individual members.
Of course, the same kind of work has to be done at some point in time no matter somewhat approach you're taking. By asking the compiler to do more work before execution, static systems usually perform better than dynamic ones. Given the downsides we've mentioned so far, you might be wondering why anyone would want to bother with dynamic typing in the first place.
When is dynamic typing useful, and why?
Dynamic typing has two important points in its favor. First, if you know the name of a member you want to call, the arguments you want to call it with, and the object you want to call it on, that's all you need. That may sound like all the information you could have anyway, but there's more that the C# compiler would normally want to know. Crucially, in order to identify the member exactly (modulo overriding) it would need to know the type of the object you're calling it on, and the types of the arguments. Sometimes you just don't know those types at compile-time, even though you do know enough to be sure that the member will be present and correct when the code actually runs.
For example, if you know that the object you're using has a
Length property you want to use, it doesn't matter whether it's a String, a StringBuilder, an Array, a Stream, or any of the other types with that property. You don't need that property to be defined by some common base class or interface - which can be useful if there isn't such a type. This is called duck typing, from the notion that "if it walks like a duck and quacks like a duck, I would call it a duck." Even when there is a type which offers everything you need, it can sometimes be an irritation to tell the compiler exactly which type you're talking about. This is particularly relevant when using Microsoft Office APIs via COM. Many method and properties are declare to just return
VARIANT, which means that C# code using these calls is often peppered with casts. Duck typing allows you to omit all of these casts, so long as you're confident about what you're doing.
The second important feature of dynamic typing is the ability of objects and types to respond to a call by analysing the name and arguments provided to it. It can behave as if the member had been declared by the type in the normal way, even if the member names couldn't possibly be known until execution time. For example, consider the following call:
Normally this would require the
FindByAuthor member to be declared by the designer of the type involved. In a dynamic data layer there can be a single smart piece of code which works out that when make a call like that and there's an
Author property in the associated data (whether that's from a database, XML document, hard-coded data or anything else) then you probably want to do a query using the specified argument as the author to find. In some ways this is just a more complex way of writing something like:
However, the first snippet feels more appropriate: the calling code knows the "Author" part statically, even if the receiving code doesn't. This approach can be used to mimic domain specific languages (DSLs) in some situations. It can also be used to create a natural API for exploring data structures such as XML trees.
Another feature of programming with dynamic languages tends to be an experimental style of programming using an appropriate interpreter, as I mentioned earlier. This isn't directly relevant to C# 4, but the fact that C# 4 can interoperate richly with dynamic languages running on the DLR (Dynamic Language Runtime) means that if you're dealing with a problem which would benefit from this style, you'll be able to use the results directly from C# instead of having to port it to C# afterwards.
We'll look at these scenarios in more depth when we've learned the basics of C# 4's dynamic abilities, so we can see more concrete examples. It's worth briefly point out that if these benefits don't apply to you, dynamic typing is more likely to be a hindrance than a help. Many developers won't need to use dynamic typing very much in their day-to-day coding, and even when it is required it may well only be for a small part of the code. Just like any feature, it can be overused; in my view it's usually worth thinking carefully about whether any alternative designs would allow static typing to solve the same problem elegantly. However, I'm biased due to having a background in statically typed languages - it's worth reading books on dynamically typed languages such as Python and Ruby to see a wider variety of benefits than the ones I present in this article.
You're probably getting anxious to see some real code by now, so we'll just take a moment to get a very brief overview of what's going on, and then dive into some examples.
How does C# 4 provide dynamic typing?
C# 4 introduces a new type called
dynamic. The compiler treats this type differently to any normal CLR type. Any expression that uses a dynamic value causes the compiler to change its behavior in a radical way. Instead of trying to work out exactly what the code means, binding each member access appropriately, performing overload resolution and so on, the compiler just parses the source code to work out what kind of operation you're trying to perform, its name, what arguments are involved and any other relevant information. Instead of emitting IL to execute the code directly, the compiler generates code which calls into the Dynamic Language Runtime with all the required information. The rest of the work is then performed at execution time. In many ways this is similar to the differences between the code generated when converting a lambda expression to an expression tree instead of a delegate type. We'll see later that expression trees are extremely important in the DLR, and in many cases the C# compiler will use expression trees to describe the code. (In the simplest cases where there's nothing but a member invocation, there's no need for an expression tree.) When the DLR comes to bind the relevant call at execution time, it goes through a complicated process to determine what should happen. This not only has to take in the normal C# rules for method overloads and so on, but also the possibility that the object itself will want to be part of the decision, as we saw in our
FindByAuthor example earlier. Most of this happens under the hood though - the source code you write to use dynamic typing can be really simple.
The 5 minute guide to dynamic
Do you remember how many new bits of syntax were involved when you learned about LINQ? Well dynamic typing is just the opposite: there's a single contextual keyword,
dynamic, which you can use in most places that you'd use a type name. That's all the new syntax that's required, and the main rules about dynamic are easily expressed, if you don't mind a little bit of hand-waving to start with:
- An implicit conversion exists from any CLR type to
- An implicit conversion exists from
dynamic to any CLR type
- Any expression which uses a value of type dynamic is evaluated dynamically.
- The static type of any dynamically-evaluated expression is deemed to be dynamic (with the exception of explicit conversions and constructor calls - in both those cases the compiler knows the type of the result, even if it doesn't know exactly how it's going to get there)
The detailed rules are more complicated, but for the sake of this article we'll stick with the simplified version above. Listing 1 provides a complete example demonstrating each point.
Listing 1: Using dynamic to iterate through a list, concatenating strings
The result of listing 1 shouldn't come as much surprise: it writes out "First!", "Second!" and "Third!". Of course we could easily have specified the types of the
valueToAdd variables explicitly in this case, and it would all have worked in the normal way - but imagine that the variables are getting their values from other data sources instead of having them hard-coded. What would happen if we wanted to add an integer instead of a string? Listing 2 is just a slight variation, but note that we haven't changed the declaration of
valueToAdd; just the assignment expression.
Listing 2: Adding integers to strings dynamically
This time the first result is "First2" - which is hopefully what you'd expect. Using static typing, we'd have to have explicitly change the declaration of
valueToAdd from string to int. The addition operator is still building a string though. What if we changed the items to be integers as well? Let's try that one simple change, as shown in listing 3.
Listing 3: Adding integers to integers
Disaster! We're still trying to convert the result of the addition to a string. The only conversions which are allowed are the same ones which are present in C# normally, so there's no conversion from int to string. The result is an exception (at execution time, of course):
Unless you're perfect, you're likely to encounter RuntimeBinderException quite a lot when you start using dynamic typing. It's the new NullPointerException, in some ways: you're bound to come across it, but with any luck it'll be in the context of unit tests rather than customer bug reports. Anyway, we can fix it by changing the type of result to dynamic, so that the conversion isn't required anyway. Come to think of it, why bother with the result variable in the first place? Let's just call
Console.WriteLine immediately. Listing 4 shows the changes.
Listing 4: Adding integers to integer - but without the exception
Now this prints
5 as we'd expect. Changing the input data would now not only change the operator which was chosen at execution time - it would also change which overload of
Console.WriteLine was called. With the original data, it would call
Console.WriteLine(string); with the updated variables if would call
Console.WriteLine(int). The data could even contain a mixture of values, making the exact call change on every iteration!
You can use dynamic as the declared type for fields, parameters and return types as well. This is in stark contrast to the use of
var, which is restricted to local variables.
Differences between var and dynamic
In many of the examples so far, when we've really known the types at compile-time, we could have used var to declare the variables. At first glance, the two features look very similar. In both cases it looks like we're declaring a variable without specifying its type - but using dynamic we're explicitly setting the type to be dynamic. You can only use var when the compiler is able to infer the type you mean statically, and the type system really does remain entirely static.
The compiler is very smart about the information it records, and the code which then uses that information at execution time is clever too: basically it's a "mini C# compiler" in its own right. It uses whatever static type information was known at compile time to make the code behave as intuitively as possible. Other than a few details of what you can't do with dynamic typing, that's all you really need to know in order to start using it in your own code.
In the second part of this article, we'll take a look at two examples of the kind of situation where you might want to use dynamic typing: working with Office (and Excel in particular), and calling into IronPython
Get 30% discount
DotNetSlacker readers can get 30% off the full print book or ebook at www.manning.com using the promo code
dns30 at checkout.
Manning Publication publishes computer books for professionals--programmers, system administrators, designers, architects, managers and others. Our focus is on computing titles at professional levels. We care about the quality of our books. We work with our authors to coax out of them the best writi...
This author has published 33 articles on DotNetSlackers. View other articles or the complete profile here.
Please login to rate or to leave a comment.