Chapter 18. Dynamic

Older versions of C# had trouble interacting with certain kinds of programs, especially those in the Microsoft Office family. You could get the job done, but before C# 4.0, it needed a lot of effort, and the results were ugly. The problem came down to a clash of philosophies: Office embraces a dynamic style, while C# used to lean heavily toward the static style. C# 4.0 now provides better support for the dynamic style, making it much easier to program Microsoft Office and similar systems from C#.

Static Versus Dynamic

What exactly is the difference between static and dynamic? The terminology is slightly confusing because C# has a keyword called static which is unrelated, so you’ll need to put your knowledge of that static to one side for now. When it comes to the dynamic/static distinction, something is dynamic if it is decided at runtime, whereas a static feature is determined at compile type. If that sounds rather abstract, it’s because the distinction can apply to lots of different things, including the choice of which method to call, the type of a variable or an expression, or the meaning of an operator.

Let’s look at some concrete examples. The compiler is able to work out quite a lot of things about code during compilation, even code as simple as Example 18-1.

Example 18-1. Simple code with various static features

var myString = Console.ReadLine();
var modifiedString = myString.Replace("color", "colour");

We’ve used the var keyword here, so we’ve not told the compiler what type these variables have, but it can work that out for us. The Console.ReadLine() method has a return type of string, meaning that myString must be of type string—the variable’s type can never be anything else, and so we say that it has a static type. (And obviously, the same would be true for any variable declared with an explicit type—declaring myString explicitly as a string would have changed nothing.) Likewise, the compiler is able to work out that modifiedString is also a string. Any variable declared with var will have a static type.

The compiler determines other aspects of code statically besides the types of variables. For example, there are method calls. The Console.ReadLine() call is straightforward. Console is a class name, so our code has been explicit about where to find the method. Since there’s no scope for ambiguity over which method we mean, this is a static method invocation—we know at compile time exactly which method will be called at runtime.

The myString.Replace method is slightly more interesting: myString refers to a variable, not a class, so to understand which method will be invoked, we need to know what type myString is. But as we already saw, in this example, its type is known statically to be string. As it happens, there are two overloads of Replace, one that takes two string arguments and one that takes two char arguments. In this code, we are passing to string literals, so the argument types are also known statically. This means that the compiler can work out which overload we require, and bakes that choice into the compiler output—once compilation completes, the exact method that Example 18-1 invokes is fixed. All the decisions are made at compile time here, and nothing can change the decision at runtime, and this is the nature of the static style.

Dynamic features defer decisions until runtime. For example, in a language that supports dynamic method invocation, the business of working out exactly which method to run doesn’t happen until the program gets to the point where it tries to invoke the method. This means that dynamic code doesn’t necessarily do the same thing every time it runs—a particular piece of code might end up invoking different methods from time to time.

You might be thinking that we’ve seen C# features in earlier chapters that enable this. And you’d be right: virtual methods, interfaces, and delegates all provide us with ways of writing code which picks the exact method to run at runtime. Static/dynamic is more of a continuum than a binary distinction. Virtual methods are more dynamic than nonvirtual methods, because they allow runtime selection of the method. Interfaces are more dynamic than virtual methods, because an object does not have to derive from any particular base class to implement a particular interface. Delegates are more dynamic than interfaces because they remove the requirement for the target to be compatible with any particular type, or even to be an object—whereas virtual methods and interfaces require instance methods, delegates also support those marked with the static keyword. (Again, try not to get distracted by the overlap in terminology here.) As you move through each of these mechanisms, the calling code knows slightly less about called code—there’s more and more freedom for things to change at runtime.

However, these mechanisms all offer relatively narrow forms of dynamism. The distinctions just listed seem rather petty next to a language that wholeheartedly embraces a dynamic style. JavaScript, for example, doesn’t even require the caller to know exactly how many arguments the method is expecting to receive.[49] And in Ruby, it’s possible for an object to decide dynamically whether it feels like implementing a particular method at all, meaning it can decide at runtime to implement methods its author hadn’t thought to include when originally writing the code!

The Dynamic Style and COM Automation

Microsoft Office is programmable through a system called COM automation, which has an adaptable approach to argument counts. Office uses this to good effect. It offers methods which are remarkably flexible because they take an astonishing number of arguments, enabling you to control every conceivable aspect of the operation. The Office APIs are designed to be used from the Visual Basic for Applications (VBA) language, which uses a dynamic idiom, so it doesn’t matter if you leave out arguments you’re not interested in. Its dynamic method invocation can supply reasonable defaults for any missing values. But this leaves more statically inclined languages with a problem. C# 3.0 requires the number and type of arguments to be known at compile time (even with delegate invocation, the most dynamic form of method invocation available in that language). This means that you don’t get to leave out the parts you don’t care about—you are forced to provide values for every single argument.

So although the designers of Microsoft Word intended for you to be able to write code roughly like that shown in Example 18-2:

Example 18-2. Word automation as Microsoft intended

var doc = wordApp.Documents.Open("WordFile.docx", ReadOnly:true);

in C# 3.0 you would have been forced to write the considerably less attractive code shown in Example 18-3.

Example 18-3. Word automation before C# 4.0

    object fileName = @"WordFile.docx";
    object missing = System.Reflection.Missing.Value;
    object readOnly = true;
    var doc = wordApp.Documents.Open(ref fileName, ref missing, ref readOnly,
        ref missing, ref missing, ref missing, ref missing, ref missing,
        ref missing, ref missing, ref missing, ref missing, ref missing,
        ref missing, ref missing, ref missing);

Not only has C# 3.0 insisted that we supply a value for every argument (using a special “this argument intentionally left blank” value to signify our intent to provide no particular value), but it has also insisted that we stick precisely to the rules of the type system. Word has chosen about the most general-purpose representation available to ensure maximum flexibility, which is why we see ref in front of every argument—it’s keeping open the possibility of passing data back out through any of these arguments. It doesn’t care that this gives the methods an unusually complex signature, because it just assumes that we’ll be using a language whose dynamic method invocation mechanism will automatically perform any necessary conversions at runtime. But if you’re using a language with no such mechanism, such as C# 3.0, it’s all rather unpleasant.

In fact, the way COM automation works is that the target object is ultimately responsible for dealing with defaults, coercion, and so on. The real problem is that C# 3.0 doesn’t have any syntax for exploiting this—if you want to defer to the COM object, you have to use the dynamic method invocation services provided by reflection, which were described in Chapter 17. Unfortunately, doing that from C# 3.0 looks even more unpleasant than Example 18-3.

Fortunately, C# 4.0 adds new dynamic features to the language that let us write code like Example 18-2, just as Word intended.

The dynamic Type

C# 4.0 introduces a new type called dynamic. In some ways it looks just like any other type such as int, string, or FileStream: you can use it in variable declarations, or function arguments and return types, as Example 18-4 shows. (The method reads a little oddly—it’s a static method in the sense that it does not relate to any particular object instance. But it’s dynamic in the sense that it uses the dynamic type for its parameters and return value.)

Example 18-4. Using dynamic

static dynamic AddAnything(dynamic a, dynamic b)
{
    dynamic result = a + b;
    Console.WriteLine(result);
    return result;
}

While you can use dynamic almost anywhere you could use any other type name, it has some slightly unusual characteristics, because when you use dynamic, you are really saying “I have no idea what sort of thing this is.” That means there are some situations where you can’t use it—you can’t derive a class from dynamic, for example, and typeof(dynamic) will not compile. But aside from the places where it would be meaningless, you can use it as you’d use any other type.

To see the dynamic behavior in action, we can try passing in a few different things to the AddAnything method from Example 18-4, as Example 18-5 shows.

Example 18-5. Passing different types

Console.WriteLine(AddAnything("Hello", "world").GetType().Name);
Console.WriteLine(AddAnything(31, 11).GetType().Name);
Console.WriteLine(AddAnything("31", 11).GetType().Name);
Console.WriteLine(AddAnything(31, 11.5).GetType().Name);

AddAnything prints the value it calculates, and Example 18-5 then goes on to print the type of the returned value. This produces the following output:

Helloworld
String
42
Int32
3111
String
42.5
Double

The + operator in AddAnything has behaved differently (dynamically, as it were) depending on the type of data we provided it with. Given two strings, it appended them, producing a string result. Given two integers, it added them, returning an integer as the result. Given some text and a number, it converted the number to a string, and then appended that to the first string. And given an integer and a double, it converted the integer to a double and then added it to the other double.

If we weren’t using dynamic, every one of these would have required C# to generate quite different code. If you use the + operator in a situation where the compiler knows both types are strings, it generates code that calls the String.Concat method. If it knows both types are integers, it will instead generate code that performs arithmetic addition. Given an integer and a double, it will generate code that converts the integer to a double, followed by code to perform arithmetic addition. In all of these cases, it would uses the static information it has about the types to work out what code to generate to represent the expression a + b.

Clearly C# has done something quite different with Example 18-4. There’s only one method, meaning it had to produce a single piece of code that is somehow able to execute any of these different meanings for the + operator. The compiler does this by generating code that builds a special kind of object that represents an addition operation, and this object then applies similar rules at runtime to those the compiler would have used at compile time if it knew what the types were. (This makes dynamic very different from var—see the sidebar on the next page.)

So the behavior is consistent with what we’re used to with C#. The + operator continues to mean all the same things it can normally mean, it just picks the specific meaning at runtime—it decides dynamically. The + operator is not the only language feature capable of dynamic operation. As you’d expect, when using numeric types, all the mathematical operators work. In fact, most of the language elements you can use in a normal C# expression work as you’d expect. However, not all operations make sense in all scenarios. For example, if you tried to add a COM object to a number, you’d get an exception. (Specifically, a RuntimeBinderException, with a message complaining that the + operator cannot be applied to your chosen combination of types.) A COM object such as one representing an Excel spreadsheet is a rather different sort of thing from a .NET object. This raises a question: what sorts of objects can we use with dynamic?

Object Types and dynamic

Not all objects behave in the same way when you use them through the dynamic keyword. C# distinguishes between three kinds of objects for dynamic purposes: COM objects, objects that choose to customize their dynamic behavior, and ordinary .NET objects. We’ll see several examples of that second category, but we’ll start by looking at the most important dynamic scenario: interop with COM objects.

COM objects

COM objects such as those offered by Microsoft Word or Excel get special handling from dynamic. It looks for COM automation support (i.e., an implementation of the IDispatch COM interface) and uses this to access methods and properties. Automation is designed to support runtime discovery of members, and it provides mechanisms for dealing with optional arguments, coercing argument types where necessary. The dynamic keyword defers to these services for all member access. Example 18-6 relies on this.

Example 18-6. COM automation and dynamic

static void Main(string[] args)
{
    Type appType = Type.GetTypeFromProgID("Word.Application");
    dynamic wordApp = Activator.CreateInstance(appType);

    dynamic doc = wordApp.Documents.Open("WordDoc.docx", ReadOnly:true);
    dynamic docProperties = doc.BuiltInDocumentProperties;
    string authorName = docProperties["Author"].Value;
    doc.Close(SaveChanges:false);
    Console.WriteLine(authorName);
}

The first two lines in this method just create an instance of Word’s application COM class. The line that calls wordApp.Documents.Open will end up using COM automation to retrieve the Document property from the application object, and then invoke the Open method on the document object. That method has 16 arguments, but dynamic uses the mechanisms provided by COM automation to offer only the two arguments the code has provided, letting Word provide defaults for all the rest.

Although dynamic is doing some very COM-specific work here, the syntax looks like normal C#. That’s because the compiler has no idea what’s going on here—it never does with dynamic. So the syntax looks the same regardless of what happens at runtime.

If you are familiar with COM you will be aware that not all COM objects support automation. COM also supports custom interfaces, which do not support dynamic semantics—they rely on compile-time knowledge to work at all. Since there is no general runtime mechanism for discovering what members a custom interface offers, dynamic is unsuitable for dealing with these kinds of COM interfaces. However, custom interfaces are well suited to the COM interop services described in Chapter 19. dynamic was added to C# mainly because of the problems specific to automation, so trying to use it with custom COM interfaces would be a case of the wrong tool for the job. dynamic is most likely to be useful for Windows applications that provide some sort of scripting feature because these normally use COM automation, particularly those that provide VBA as their default scripting language.

Silverlight script objects

Silverlight applications can run in the web browser, which adds an important interop scenario: interoperability between C# code and browser objects. Those might be objects from the DOM, or from script. In either case, these objects have characteristics that fit much better with dynamic than with normal C# syntax, because these objects decide which properties are available at runtime.

Silverlight 3 used C# 3.0, so dynamic was not available. It was still possible to use objects from the browser scripting world, but the syntax was not quite as natural. For example, you might have defined a JavaScript function on a web page, such as the one shown in Example 18-7.

Example 18-7. JavaScript code on a web page

<script type="text/javascript">
    function showMessage(msg)
    {
        var msgDiv = document.getElementById("messagePlaceholder");
        msgDiv.innerText = msg;
    }
</script>

Before C# 4.0, you could invoke this in a couple of ways, both of which are illustrated in Example 18-8.

Example 18-8. Accessing JavaScript in C# 3.0

ScriptObject showMessage = (ScriptObject)
    HtmlPage.Window.GetProperty("showMessage");
showMessage.InvokeSelf("Hello, world");

// Or...

ScriptObject window = HtmlPage.Window;
window.Invoke("showMessage", "Hello, world");

While these techniques are significantly less horrid than the C# 3.0 code for COM automation, they are both a little cumbersome. We have to use helper methods—GetProperty, InvokeSelf, or Invoke to retrieve properties and invoke functions. But Silverlight 4 supports C# 4.0, and all script objects can now be used through the dynamic keyword, as Example 18-9 shows.

Example 18-9. Accessing JavaScript in C# 4.0

dynamic window = HtmlPage.Window;
window.showMessage("Hello, world");

This is a far more natural syntax, so much so that the second line of code happens to be valid JavaScript as well as being valid C#. (It’s idiomatically unusual—in a web page, the window object is the global object, and so you’d normally leave it out, but you’re certainly allowed to refer to it explicitly, so if you were to paste that last line into script in a web page, it would do the same thing as it does in C#.) So dynamic has given us the ability to use JavaScript objects in C# with a very similar syntax to what we’d use in JavaScript itself—it doesn’t get much more straightforward than that.

Note

The Visual Studio tools for Silverlight do not automatically add a reference to the support library that enables dynamic to work. So when you first add a dynamic variable to a Silverlight application, you’ll get a compiler error. You need to add a reference to the Microsoft.CSharp library in your Silverlight project. This applies only to Silverlight projects—other C# projects automatically have a reference to this library.

Ordinary .NET objects

Although the dynamic keyword was added mainly to support interop scenarios, it is quite capable of working with normal .NET objects. For example, if you define a class in your project in the normal way, and create an instance of that class, you can use it via a dynamic variable. In this case, C# uses .NET’s reflection APIs to work out which methods to invoke at runtime. We’ll explore this with a simple class, defined in Example 18-10.

Example 18-10. A simple class

class MyType
{
    public string Text { get; set; }
    public int Number { get; set; }

    public override string ToString()
    {
        return Text + ", " + Number;
    }

    public void SetBoth(string t, int n)
    {
        Text = t;
        Number = n;
    }

    public static MyType operator + (MyType left, MyType right)
    {
        return new MyType
        {
            Text = left.Text + right.Text,
            Number = left.Number + right.Number
        };
    }
}

We can use objects of this through a dynamic variable, as Example 18-11 shows.

Example 18-11. Using a simple object with dynamic

dynamic a = new MyType { Text = "One", Number = 123 };
Console.WriteLine(a.Text);
Console.WriteLine(a.Number);
Console.WriteLine(a.Problem);

The lines that call Console.WriteLine all use the dynamic variable a with normal C# property syntax. The first two do exactly what you’d expect if the variable had been declared as MyType or var instead of dynamic: they just print out the values of the Text and Number properties. The third one is more interesting—it tries to use a property that does not exist. If the variable had been declared as either MyType or var, this would not have compiled—the compiler would have complained at our attempt to read a property that it knows is not there. But because we’ve used dynamic, the compiler does not even attempt to check this sort of thing at compile time. So it compiles, and instead it fails at runtime—that third line throws a RuntimeBinderException, with a message complaining that the target type does not define the Problem member we’re looking for.

This is one of the prices we pay for the flexibility of dynamic behavior: the compiler is less vigilant. Certain programming errors that would be caught at compile time when using the static style do not get detected until runtime. And there’s a related price: IntelliSense relies on the same compile-time type information that would have noticed this error. If we were to change the variable in Example 18-11’s type to either MyType or var, we would see IntelliSense pop ups such as those shown in Figure 18-1 while writing the code.

Visual Studio is able to show the list of available methods because the variable is statically typed—it will always refer to a MyType object. But with dynamic, we get much less help. As Figure 18-2 shows, Visual Studio simply tells us that it has no idea what’s available. In this simple example, you could argue that it should be able to work it out—although we’ve declared the variable to be dynamic, it can only ever be a MyType at this point in the program. But Visual Studio does not attempt to perform this sort of analysis for a couple of reasons. First, it would work for only relatively trivial scenarios such as these, and would fail to work anywhere you were truly exploiting the dynamic nature of dynamic—and if you don’t really need the dynamism, why not just stick with statically typed variables? Second, as we’ll see later, it’s possible for a type to customize its dynamic behavior, so even if Visual Studio knows that a dynamic variable always refers to a MyType object, that doesn’t necessarily mean that it knows what members will be available at runtime. Another upshot is that with dynamic variables, IntelliSense provides the rather less helpful pop up shown in Figure 18-2.

IntelliSense with a statically typed variable

Figure 18-1. IntelliSense with a statically typed variable

IntelliSense with a dynamically typed variable

Figure 18-2. IntelliSense with a dynamically typed variable

Example 18-11 just reads the properties, but as you’d expect, we can set them, too. And we can also invoke methods with the usual syntax. Example 18-12 illustrates both features, and contains no surprises.

Example 18-12. Setting properties and calling methods with dynamic

dynamic a = new MyType();
a.Number = 42;
a.Text = "Foo";
Console.WriteLine(a);
dynamic b = new MyType();
b.SetBoth("Bar", 99);
Console.WriteLine(b);

Our MyType example also overloads the + operator—it defines what should occur when we attempt to add two of these objects together. This means we can take the two objects from Example 18-12 and pass them to the AddAnything method from Example 18-4, as Example 18-13 shows.

Example 18-13. Using an overloaded + operator

MyType c = AddAnything(a, b);
Console.WriteLine(c);

Recall that Example 18-4 just uses the normal C# syntax for adding two things together. We wrote that code before even writing the MyType class, but despite this, it works just fine—it prints out:

FooBar, 141

The custom + operator in MyType concatenates the Text properties and adds the Number properties, and we can see that’s what’s happened here. Again, this shouldn’t really come as a surprise—this is another example of the basic principle that operations should work the same way when used through dynamic as they would statically.

Example 18-13 illustrates another feature of dynamic—assignment. You can, of course, assign any value into a variable of type dynamic, but what’s more surprising is that you can also go the other way—you are free to assign an expression of dynamic type into a variable of any type. The first line of Example 18-13 assigns the return value of AddAnything into a variable of type MyType. Recall that AddAnything has a return type of dynamic, so you might have thought we’d need to cast the result back to MyType here, but we don’t. As with all dynamic operations, C# lets you try whatever you want at compile time and then tries to do what you asked at runtime. In this case, the assignment succeeds because AddAnything ended up adding two MyType objects together to return a reference to a new MyType object. Since you can always assign a reference to a MyType object into a MyType variable, the assignment succeeds. If there’s a type mismatch, you get an exception at runtime. This is just another example of the same basic principle; it’s just a bit subtler because assignment is usually a trivial operation in C#, so it’s not immediately obvious that it might fail at runtime.

While most operations are available dynamically, there are a couple of exceptions. You cannot invoke methods declared with the static keyword via dynamic. In some ways, this is unfortunate—it could be useful to be able to select a particular static (i.e., noninstance) method dynamically, based on the type of object you have. But that would be inconsistent with how C# works normally—you are not allowed to invoke static methods through a statically typed variable. You always need to call them via their defining type (e.g., Console.WriteLine). The dynamic keyword does not change anything here.

Extension methods are also not available through dynamic variables. On the one hand, this makes sense because extension methods are really just static methods disguised behind a convenient syntax. On the other hand, that convenient syntax is designed to make it look like these are really instance methods, as Example 18-14 shows.

Example 18-14. Extension methods with statically typed variables

using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        IEnumerable<int> numbers = Enumerable.Range(1, 10);
        int total = numbers.Sum();
    }
}

The call to numbers.Sum() makes it look like IEnumerable<int> defines a method called Sum. In fact there is no such method, so the compiler goes looking for extension methods—it searches all of the types in all of the namespaces for which we have provided using directives. (That’s why we’ve included the whole program here rather than just a snippet—you need the whole context including the using System.Linq; directive for that method call to make sense.) And it finds that the Enumerable type (in the System.Linq namespace) offers a suitable Sum extension method.

If we change the first line in the Main method to the code shown in Example 18-15, things go wrong.

Example 18-15. Replacing IEnumerable<int> with dynamic

dynamic numbers = Enumerable.Range(1, 10);

The code still compiles, but at runtime, when we reach the call to Sum, it throws a RuntimeBinderException complaining that the target object does not define a method called Sum.

So, in this case, C# has abandoned the usual rule of ensuring that the runtime behavior with dynamic matches what statically typed variables would have delivered. The reason is that the code C# generates for a dynamic call does not contain enough context. To resolve an extension method, it’s necessary to know which using directives are present. In theory, it would have been possible to make this context available, but it would significantly increase the amount of information the C# compiler would need to embed—anytime you did anything to a dynamic variable, the compiler would need to ensure that a list of all the relevant namespaces was available. And even that wouldn’t be sufficient—at compile time, C# only searches for extension methods in the assemblies your project references, so to deliver the same method resolution semantics at runtime that you get statically would require that information to be made available too.

Worse, this would prevent the C# compiler from being able to optimize your project references. Normally, C# detects when your project has a reference to an assembly that your code never uses, and it removes any such references at compile time.[50] But if your program made any dynamic method calls, it would need to keep references to apparently unused assemblies, just in case they turn out to be necessary to resolve an extension method call at runtime.

So while it would have been possible for Microsoft to make this work, there would be a significant price to pay. And it would probably have provided only marginal value, because it wouldn’t even be useful for the most widely used extension methods. The biggest user of extension methods in the .NET Framework class library is LINQ—that Sum method is a standard LINQ operator, for example. It’s one of the simpler ones. Most of the operators take arguments, many of which expect lambdas. For those to compile, the C# compiler depends on static type information to create a suitable delegate. For example, there’s an overload of the Sum operator that takes a lambda, enabling you to compute the sum of a value calculated from the underlying data, rather than merely summing the underlying data itself. Example 18-16 uses this overload to calculate the sum of the squares of the numbers in the list.

Example 18-16. Lambdas and types

int total = numbers.Sum(x => x * x);

When the numbers variable has a static type (IEnumerable<int> in our case) this works just fine. But if numbers is dynamic, the compiler simply doesn’t have enough information to know what code it needs to generate for that lambda. Given sufficiently heroic efforts from the compiler, it could embed enough information to be able to generate all the necessary code at runtime, but for what benefit? LINQ is designed for a statically typed world, and dynamic is designed mainly for interop. So Microsoft decided not to support these kinds of scenarios with dynamic—stick with static typing when using LINQ.

Objects from other dynamic languages

The dynamic keyword uses an underlying mechanism that is not unique to C#. It depends on a set of libraries and conventions known as the DLR—the Dynamic Language Runtime. The libraries are built into the .NET Framework, so these services are available anywhere .NET 4 or later is available. This enables C# to work with dynamic objects from other languages.

Earlier in this chapter, we mentioned that in the Ruby programming language, it’s possible to write code that decides at runtime what methods a particular object is going to offer. If you’re using an implementation of Ruby that uses the DLR (such as IronRuby), you can use these kinds of objects from C#. The DLR website provides open source implementations of two languages that use the DLR: IronPython and IronRuby (see http://dlr.codeplex.com/).

ExpandoObject

The .NET Framework class library includes a class called ExpandoObject, which is designed to be used through dynamic variables. It chooses to customize its dynamic behavior. (It does this by implementing a special interface called IDynamicMetaObjectProvider. This is defined by the DLR, and it’s also the way that objects from other languages are able to make their language-specific dynamic behavior available to C#.) If you’re familiar with JavaScript, the idea behind ExpandoObject will be familiar: you can set properties without needing to declare them first, as Example 18-17 shows.

Example 18-17. Setting dynamic properties

dynamic dx = new ExpandoObject();

dx.MyProperty = true;
dx.AnotherProperty = 42;

If you set a property that the ExpandoObject didn’t previously have, it just grows that as a new property, and you can retrieve the property later on. This behavior is conceptually equivalent to a Dictionary<string, object>, the only difference being that you get and set values in the dictionary using C# property accessor syntax, rather than an indexer. You can even iterate over the values in an ExpandoObject just as you would with a dictionary, as Example 18-18 shows.

Example 18-18. Iterating through dynamic properties

foreach (KeyValuePair<string, object> prop in dx)
{
    Console.WriteLine(prop.Key + ": " + prop.Value);
}

If you are writing C# code that needs to interoperate with another language that uses the DLR, this class can be convenient—languages that fully embrace the dynamic style often use this sort of dynamically populated object in places where a more statically inclined language would normally use a dictionary, so ExpandoObject can provide a convenient way to bridge the gap. ExpandoObject implements IDictionary<string, object>, so it speaks both languages. As Example 18-19 shows, you add properties to an ExpandoObject through its dictionary API and then go on to access those as dynamic properties.

Example 18-19. ExpandoObject as both dictionary and dynamic object

ExpandoObject xo = new ExpandoObject();

IDictionary<string, object> dictionary = xo;
dictionary["Foo"] = "Bar";

dynamic dyn = xo;
Console.WriteLine(dyn.Foo);

This trick of implementing custom dynamic behavior is not unique to ExpandoObject—we are free to write our own objects that do the same thing.

Custom dynamic objects

The DLR defines an interface called IDynamicMetaObjectProvider, and objects that implement this get to define how they behave when used dynamically. It is designed to enable high performance with maximum flexibility, which is great for anyone using your type, but it’s a lot of work to implement. Describing how to implement this interface would require a fairly deep discussion of the workings of the DLR, and is beyond the scope of this book. Fortunately, a more straightforward option exists.

The System.Dynamic namespace defines a class called DynamicObject. This implements IDynamicMetaObjectProvider for you, and all you need to do is override methods representing whichever operations you want your dynamic object to support. If you want to support dynamic properties, but you don’t care about any other dynamic features, the only thing you need to do is override a single method, TryGetMember, as Example 18-20 shows.

Example 18-20. Custom dynamic object

using System;
using System.Dynamic;

public class CustomDynamic : DynamicObject
{
    private static DateTime FirstSighting = new DateTime(1947, 3, 13);

    public override bool TryGetMember(GetMemberBinder binder,
                                      out object result)
    {
        var compare = binder.IgnoreCase ?
            StringComparer.InvariantCultureIgnoreCase :
            StringComparer.InvariantCulture;
        if (compare.Compare(binder.Name, "Brigadoon") == 0)
        {
            // Brigadoon famous for appearing only once every hundred years.
            DateTime today = DateTime.Now.Date;
            if (today.DayOfYear == FirstSighting.DayOfYear)
            {
                // Right day, what about the year?
                int yearsSinceFirstSighting = today.Year - FirstSighting.Year;
                if (yearsSinceFirstSighting % 100 == 0)
                {
                    result = "Welcome to Brigadoon. Please drive carefully.";
                    return true;
                }
            }
        }
        return base.TryGetMember(binder, out result);
    }
}

This object chooses to define just a single property, called Brigadoon.[51] Our TryGetMember will be called anytime some code attempts to read a property from our object. The GetMemberBinder argument provides the name of the property the caller is looking for, so we compare it against our one and only supported property name. The binder also tells us whether the caller prefers a case-sensitive comparison—in C# IgnoreCase will be false, but some languages (such as VB.NET) prefer case-insensitive comparisons. If the name matches, we then decide at runtime whether the property should be present or not—this particular property is available for only a day at a time once every 100 years. This may not be hugely useful, but it illustrates that objects may choose whatever rules they like for deciding what properties to offer.

Note

If you’re wondering what you would get in exchange for the additional complexity of IDynamicMetaObjectProvider, it makes it possible to use caching and runtime code generation techniques to provide high-performance dynamic operation. This is a lot more complicated than the simple model offered by DynamicObject, but has a significant impact on the performance of languages in which the dynamic model is the norm.

dynamic in Noninterop Scenarios?

The main motivation behind dynamic’s introduction was to make it possible to use Office without writing horrible code. It also has uses in other interop scenarios such as dealing with browser script in Silverlight, and working with dynamic languages. But would you ever use it in a pure C# scenario? The dynamic style has become increasingly fashionable in recent years—some popular JavaScript libraries designed for client-side web code make cunning use of dynamic idioms, as do certain web frameworks. Some developers even go as far as to claim that a dynamic style is inherently superior to a static style. If that’s the way the wind is blowing, should C# developers follow this trend?

Tantalizingly, for those keen on dynamic languages, dynamic has brought some dynamic language features to C#. However, the key word here is some. C# 4.0 added dynamic to improve certain interop scenarios, not to support whole new programming idioms. It is therefore not helpful to think of dynamic in terms of “dynamic extensions for C#.”

If you attempt to use C# as though it were a fully fledged dynamic language, you’ll be stepping outside the language’s core strength, so you will inevitably run into problems. We’ve already seen a LINQ example that did not mix well with dynamic, and that failure was a symptom of a broader problem. The underlying issue is that delegates are not as flexible as you might expect when it comes to dynamic behavior. Consider the method shown in Example 18-21.

Example 18-21. A simple filter

static bool Test(int x)
{
    return x < 100;
}

We can use this in conjunction with the LINQ Where operator, as Example 18-22 shows.

Example 18-22. Filtering with LINQ

var nums = Enumerable.Range(1, 200);
var filteredNumbers = nums.Where(Test);

What if we wanted to make this more general-purpose? We could modify Test so that instead of working only with int, it works with any built-in numeric type, or indeed any type that offers a version of the < operator that can be used with int. We could do that by changing the argument to dynamic, as Example 18-23 shows.

Example 18-23. A dynamic filter

static bool Test(dynamic x)
{
    return x < 100;
}

Unfortunately, this change would cause the code in Example 18-22 to fail with a compiler error. It complains that there are no overloads that match delegate System.Func<int,bool>, which is the function type the Where method expects here. This is frustrating because our Test method is certainly capable of accepting an int and returning a bool, but despite this, we need to add our own wrapper. Example 18-24 does the job.

Example 18-24. Making a dynamic filter palatable for LINQ

var filteredNumbers = nums.Where(x => Test(x));

This is a little weird because it seems like it should mean exactly the same as the equivalent line in Example 18-22. We’ve had to add some extra code just to keep the C# type system happy, and normally that’s exactly the sort of thing the dynamic style is supposed to let you avoid. Part of the problem here is that we’re trying to use LINQ, a thoroughly static-oriented API. But it turns out that there’s a deeper problem here, which we can illustrate by trying to write our own dynamic-friendly version of Where. Example 18-25 will accept anything as its test argument. This DynamicWhere method will be happy as long as test can be invoked as a method that returns a bool (or something implicitly convertible to bool).

Example 18-25. A dynamic-friendly Where implementation

static IEnumerable<T> DynamicWhere<T>(IEnumerable<T> input, dynamic test)
{
    foreach (T item in input)
    {
        if (test(item))
        {
            yield return item;
        }
    }
}

This compiles, and will behave as intended if you can manage to invoke it, but unfortunately it doesn’t help. Example 18-26 tries to use this, and it will not compile.

Example 18-26. Attempting (and failing) to call DynamicWhere

var filteredNumbers = DynamicWhere(nums, Test);  // Compiler error

The C# compiler complains:

Argument 2: cannot convert from 'method group' to 'dynamic'

The problem is that we’ve given it too much latitude. Example 18-25 will work with a wide range of delegate types. It would be happy with Predicate<object>, Predicate<dynamic>, Predicate<int>, Func<object, bool>, Func<dynamic, bool>, or Func<int, bool>. Or you could define a custom delegate type of your own that was equivalent to any of these. The only thing the C# compiler can see is that DynamicWhere expects a dynamic argument, so for all it knows, it could pass any type at all. All it would have to do is pick one that fits the Test method’s signature—any delegate type with a single argument and a return type of bool would do. But it doesn’t have any rule to say which particular delegate type to use by default here.

In Example 18-22, the compiler knew what to do because the Where method expected a specific delegate type: Func<int, bool>. Since there was only one possible option, the C# compiler was able to create a delegate of the right kind. But now that it has too much choice, we need to narrow things down again so that it knows what to do. Example 18-27 shows one way to do this, although you could cast to any of the delegate types mentioned earlier.

Example 18-27. Giving DynamicWhere a clue

var filteredNumbers = DynamicWhere(nums, (Predicate<dynamic>) Test);

Again, we’ve ended up doing extra work just to satisfy the C# type system, which is the opposite of what you’d usually expect in the dynamic idiom—types are supposed to matter less.

This is exactly the sort of problem you’ll run into if you attempt to treat C# as a dynamic programming language—the underlying issue here is that dynamic was designed to solve specific interop problems. It does that job very well, but C# as a whole is not really at home in the dynamic style. So it’s not a good idea to attempt to make heavy use of that style in your C# code.

Summary

C# 4.0’s new dynamic keyword makes it much easier to use objects that were designed to be used from dynamic programming languages. In particular, COM automation APIs such as those offered by the Microsoft Office suite are far more natural to use than they have been in previous versions of the language. Interoperating with browser script objects in Silverlight is also easier than before.



[49] Yes, so C# supports variable-length argument lists, but it fakes it. Such methods really have a fixed number of arguments, the last of which happens to be an array. There is only one variable-length Console.WriteLine method, and the compiler is able to determine statically when you use it.

[50] This optimization doesn’t occur for Silverlight projects, by the way. The way Silverlight uses control libraries from Xaml means Visual Studio has to be conservative about project references.

[51] According to popular legend, Brigadoon is a Scottish village which appears for only one day every 100 years.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset