At last week’s //build/ conference Microsoft showed off the new version of their flagship operating system, Windows 8. This is a landmark upgrade for the software that runs the great majority of consumer and corporate desktop and laptop computers around the world. As a major component of this new release, Microsoft was also showing off the new “Metro” UI - a new way of interacting with computers that support “touch-enabled” hardware.

image

In the keynote presentation where this new user interface was unveiled, Microsoft executive Steve Sinofsky spoke about a future where non-touch devices will seem “quaint” and where rich touch-based interactivity will be the norm. Clearly, touch-enabled devices are the focus for Windows 8, and the company is making a huge bet on their Metro design. To drive this point home, the company announced that all conference attendees would be receiving their own tablet computers, complete with peripherals and generous hardware specifications. Of course, these new tablets are all pre-loaded with a “developer preview” of the Windows 8 operating system.

And I must say, this new Metro design is very elegant and intuitive. The internet has been abuzz with talk about how “alive” and “inviting” this new user experience is, and how well it performs (particularly surprising given the very early nature of the version which was shown).

And so 5,000 or so people have been tinkering with their shiny new tablet computers for the past week. It was discovered quickly that the “semantic zoom” feature of the Start screen demo’d in the keynote was disabled for now (to be activated in a future update). Other quirks and bugs have been found too, as to be expected with such an early preview build of a brand new operating system. My personal highest-priority “issue” is in the lack of a traditional Start Menu when in “classic” desktop mode. This makes it very difficult for power users to navigate their installed applications, and I am sure Microsoft has received that feedback many times over. But so far, the media and the “twitterverse” in general seem to not be so concerned with those problems up until now. Instead, they are fixated on something totally different: the Internet Explorer 10 browser that will ship with Windows 8, the fact that there are two versions of the browser present, and the absence of Flash support under the Metro version.

 

A Tale of Two Browsers

Everyone pretty much agrees that the Metro UI is fantastic, especially for the consumer market. Even my dad (who generally avoids technology of all forms) was open to playing with my tablet for a few minutes. It also seems to have quickly passed my wife’s acceptance test. One of the first things people tend to want to try out is the web browser, and IE10 under Metro is beautiful. It works very smoothly. Web pages are visually sharp and bright with color, and the browser is fast. It responds immediately and accurately to touch gestures like scrolling and flicking.

imageAnd then it happens.

They reach a page that uses Flash. Or Silverlight. And it doesn’t work. In the best scenario the website gracefully degrades to an HTML-only version of the content. In the worst case scenario it tells the user they need to install Flash/Silverlight (a message many users haven’t seen in a very long time). And of course, when they attempt to download Flash or Silverlight, they discover that it isn’t available for their browser.

image

If the user wants (or needs) Flash or Silverlight support, then the only option right now is to switch over to the “classic desktop” and use the 32-bit desktop version of the IE10 browser instead. Those plugins work under that browser. But it isn’t nearly as pretty and fast as IE10 Metro.

 

“I knew it! Microsoft is trying to kill off plugin vendors”

Don’t bring out the tin-foil hats just yet. I don’t believe Microsoft is trying to pull anything over on us. My reasoning is two-fold. First, the Metro version of IE10 (there are two applications named “IE10” in Windows 8) is a 64-bit browser, and existing IE plugins are only available in 32-bit format. They cannot run in this browser because they are incompatible down at the CPU instruction level. However this is a relatively minor setback and plugins can be recompiled (after some likely compatibility changes), and this would resolve this particular issue.

But there is a bigger reason why we cannot run browser plugins in IE10 Metro. If you are a Windows developer, then by now you are probably familiar with the infamous “boxology” slide from the keynote presentation that was shown countless more times in the breakout sessions during the remainder of the conference. If you are not familiar with this slide, here it is:

image

Note the position of HTML/CSS and JavaScript in this diagram. These two boxes (if you combine them) form IE10 Metro. They are also the underlying plumbing that supports HTML5 Metro Applications - this is the same rendering technology. The importance of this is that IE10 clearly rests on top of the Windows Runtime (WinRT) stack, and specifically the Graphics and Media portions of that stack.

Here is another slide from the conference. This one is from one of the sessions with lower popularity, so not many were there to catch it. It provides a little more detail about what lives underneath those XAML and HTML layers:

image

This presentation was about the 3D graphics stack in Windows 8 (specifically, it was on the subjects of Direct3D and Direct2D). According to this architecture diagram, the HTML rendering components are build on top of DirectX technology. This makes a lot of sense actually, and it certainly explains why IE10 Metro is so darned fast and why Metro in general can be so power-efficient (no DWM composition to worry about). In fact, at one point during the presentation, the speaker declared “JavaScript developers are now GPU developers”.

And so in Christian Schormann’s presentation using Blend to design HTML Metro applications, when he said that the CSS effects were hardware accelerated, he wasn’t kidding. IE10 Metro uses DirectX for drawing to the screen. And so do all other Metro apps that are built on top of the new XAML and HTML stacks.

 

Getting back to my point

So we know that Internet Explorer 10 Metro is built using DirectX graphics technology. Why is this important? And what does it have to do with Flash and Silverlight?

The answer is simple really. Drawing to a DirectX buffer is very different from drawing to a typical desktop window surface. In a normal desktop application we are typically responding to the operating system’s requests to redraw portions of our UI by responding to WM_PAINT messages. The Desktop Window Manager (DWM) manages the composition of our window, along with any “chroming” artwork, with the other windows visible on the desktop. This is a fairly memory-intensive mechanism, and because of the many layers of graphics rendering needed to get the end result, the performance tends to not be stellar.

On the other hand, DirectX applications have no notion of a composition manager. There is no DWM. As some developers have quickly noticed, there is no support for popup dialog windows of any kind. There is only one “window”, and it is always full-screen. The application draws directly and efficiently to a memory buffer that is directly flipped over to the graphics pipeline when needed. It is very efficient and very fast. But the downside is that it also removes all barriers between the application and the GPU. Rogue rendering code (or buggy code) can easily corrupt the display buffers, destabilizing the entire DirectX environment. The WinRT team can be fairly certain that their XAML and HTML rendering code is robust, but they have no such guarantees when it comes to third-party code that might be running within plugins.

As a result, it just isn’t safe to allow plugins for IE10 Metro. When the GUI crashes because of a bad plugin, the end user will not blame the plugin - they will blame Windows. So unless the IE team figures out a way to properly sandbox the plugins, or unless the DirectX team is able to provide protection against crashes from external code, I really just don’t see it ever being possible to support plugins from IE10 Metro.

But as many have pointed out, we still have IE10 Classic for those scenarios.

 

In a nutshell, IE10 Metro does not support Flash and Silverlight because it cannot support them. Not because Microsoft is just being evil.

It’s no secret that Silverlight and WPF share a lot in common, primarily a result of their shared CLR Framework heritage. I have noticed a considerable increase in the number of organizations that are building applications that target both platforms. And in particular I have seen a rise in the number of organizations that are building a single application for both platforms while sharing as much code between them as possible. The results vary, some are more successful than others.

Far and away the most common approach I see is the use of the Linked Files feature in Visual Studio. We create a new Class Library project and within it we write code normally. Then a Silverlight Class Library project is added to the solution and we simply add all of the *.cs files from the normal Class Library projects using the “Add Existing Item… Add As Linked” process within Visual Studio. We typically use conditional directives (#if SILVERLIGHT) to handle small variances between the two platforms. When we build our solution we get two assemblies - one for desktop/WPF applications and one for Silverlight applications. Pretty straightforward and easy to do.

However there are some drawbacks to this.

  1. First, this technique is incompatible with wildcard inclusions, which was a major hurdle for a recent project of mine.
  2. Second, the compiler itself seems to have trouble with Linked Files. When a compiler error is encountered, you can double-click the error to jump directly to the source line. However Visual Studio doesn’t know if you want to go to the original file or the linked file. This matters because the linked version will have different assembly references and conditional #defines. Visual Studio has a 50/50 chance of picking the right one, but in my experience it almost always guesses poorly.

So in this article I would like to show a different approach that works around those issues. Like my prior article on Wildcard Inclusions, this approach will require manual project file editing since it leverages features of msbuild that are not surfaced adequately through the Visual Studio UI.

Let’s get started.

Using Custom Build Targets

This approach leverages the Custom Build Targets feature of Visual Studio. I would be willing to bet that most developers are fully unaware of it aside from the occasional switching between the default Debug and Release on their build toolbar. And let’s be honest here. I would bet that more than 50% of developers never even build to anything except for the Debug target (even when creating a Production build for deployment).

Instead of adding a new “Silverlight” project with Linked File references, we will use a single project file and distinct “DesktopDebug/DesktopRelease” and “SilverlightDebug/SilverlightRelease” build targets to handle the builds for each platform. The first issue I listed above will be resolved because we are no longer using Linked File Items in the project file. And the second issue will no longer be a problem because Visual Studio will not be confused by having multiple ways to reach the same physical source file from different projects.

Start with a Desktop CLR Project

To demonstrate this technique we can create a very simple starter project. Obviously, a real scenario would include considerably more code than this.

First, create new Windows C# Class Library, names of the project and solution container are unimportant. For this example I chose “MyClassLibrary”. This project template creates a default source code file named Class1.cs with a stub for Class1. This is fine for our purposes here, so let’s insert some code to demonstrate that our dual-target configuration is working properly. We can do this by using a conditional directive to include/exclude code based on the presence of the “SILVERLIGHT” conditional. This conditional is only defined when compiling for a Silverlight target environment.

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace MyClassLibrary
  7. {
  8.     public class Class1
  9.     {
  10. #if SILVERLIGHT
  11.         const string Foo = "Hello, Silverlight CLR!";
  12. #else
  13.         const string Foo = "Hello, Desktop CLR!";
  14. #endif
  15.     }
  16. }

When a Silverlight build target is active the first version of the Foo constant will be compiled. When a Desktop build target is active, the second version will be compiled. This should be simple enough to demonstrate the concept.

Modify the Build Target Configuration

Select “Configuration Manager” from the build target dropdown on your toolbar to launch the Configuration Manager dialog:

image

Select “<Edit…>” from the Active Solution Configurations dropdown. This will display the Edit Solution Configurations dialog where you can Rename the existing configurations.

image

Rename “Debug” to “DesktopDebug” and rename “Release” to “DesktopRelease”. These are the build configurations you will want to use when debugging Desktop-specific code.

image

Next, we want to add the new Build Configurations to support Silverlight compilation. Under the Active Solution Configurations dropdown select the “<New…>” option to bring up the New Solution Configuration dialog. First we will create the SilverlightDebug configuration, so enter that for the Name field. For the Copy Settings From option, select DesktopDebug from the list. Be sure to check the “Create new project configurations” option. This will enable each configuration to use a different set of references and conditionals. Follow the same steps to copy the DesktopRelease settings to a new SilverlightRelease configuration:

image

We should now see all four configurations listed in Visual Studio’s Build Toolbar:

image

You will use this dropdown to switch build output targets when debugging/compiling.

There is one more step to be performed using the Visual Studio configuration tools. Right-click the project node in Solution Explorer and bring up the Properties page for our Class Library project. Alternatively, you could select “MyClassLibrary Properties…” from the Project menu. On the Build tab you will see that there are no Conditional Compilation Symbols defined (the setting is blank). Use the Build Configuration dropdown to switch to the SilverlightDebug build target. Then enter “SILVERLIGHT” in the Conditional Compilation Symbols textbox. Switch again to SilverlightRelease and enter the same value. You should be able to see that this value is unchanged for the Desktop builds by switching to those build targets too (the Properties page updates when you select a new build target).

image

Note that you may also want to customize the Build Output Path for the Silverlight build targets so that they get directed to a different output folder when switching build targets. Otherwise the Silverlight output will overwrite the Desktop output and vice versa. It just keeps things ever so slightly more tidy.

That is all we need in order to set up the environment. However there is still one more task to perform, and unfortunately the Visual Studio IDE will not help us with this part. We need to configure our project to use the Silverlight Core assemblies when compiling to the Silverlight build targets.

Modify the Project File to use Silverlight References

Right-click the project node in Visual Studio’s Solution Explorer pane and select “Unload Project” (or select “Unload Project” from the Project menu). The project for MyClassLibrary should now show up as unavailable. Right-click it again, and select “Edit MyClassLibrary.csproj” this time to open up the project source file directly in Visual Studio’s XML editor.

The first thing we need to do here is apply some magic fairy dust. MSBuild will automatically pull in references to essential CLR assemblies (such as System.Core), but we don’t want this behavior for our Silverlight build targets which need to reference a different version tailored for that runtime. This is not very hard to do. At the very top of your project file, just insert the following PropertyGroup XML element, as the first child element of the Project element:

  1. <PropertyGroup>
  2.   <NoConfig>true</NoConfig>
  3.   <AddAdditionalExplicitAssemblyReferences>false</AddAdditionalExplicitAssemblyReferences>
  4. </PropertyGroup>

The next thing we want to do is create some substitution variables that point to your Silverlight installation folder and your SDK folder. These paths will be used by the compiler when building your assembly (these paths are only used by the compiler to find the right DLLs to build against). Be sure to update these paths if you have installed them to another location. On my system these are in the default locations, but your system might be different, so verify the path values. Also, it is possible - likely even - that in a real build scenario you might want these paths pointing to a folder that is itself under source control. In that case you would use the substitution variable $(MSBuildThisFileDirectory) in the path to have msbuild figure out the absolute path for you. For this example I am just hard-coding the SDK and Runtime paths:

  1. <PropertyGroup Condition="'$(Configuration)' == 'SilverlightRelease' or '$(Configuration)' == 'SilverlightDebug'">
  2.   <SilverlightPath Condition="'$(SilverlightPath)' == ''">C:\Program Files (x86)\Microsoft Silverlight\4.0.60310.0</SilverlightPath>
  3.   <SilverlightSdkPath Condition="'$(SilverlightSdkPath)' == ''">C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Libraries\Client</SilverlightSdkPath>
  4.   <SilverlightBuild>true</SilverlightBuild>
  5.   <NoStdLib>true</NoStdLib>
  6. </PropertyGroup>

This PropertyGroup should be placed after the first element that we inserted in the prior step.

In addition to setting up those substitution variables, in the above XML we are also setting a new variable named “SilverlightBuild” to “true” and preventing the compiler from including standard libraries. Note the use of a Conditional that only sets these options when the current Configuration is set to SilverlightRelease or SilverlightDebug.

After setting up these substitution variables and other build options, the only thing left is to tell the compiler which assemblies we really want it to use instead of the defaults. Scroll down towards the bottom of the project file and you will find an ItemGroup element that contains a number of Reference elements. These are not appropriate for Silverlight, so remove that ItemGroup section and replace it with the following two sections (the first will be used when compiling to Desktop, the second when compiling to Silverlight):

  1. <ItemGroup Condition=" '$(SilverlightBuild)' != 'true' ">
  2.   <Reference Include="System" />
  3.   <Reference Include="System.Core" />
  4.   <Reference Include="System.Xml.Linq" />
  5.   <Reference Include="System.Data.DataSetExtensions" />
  6.   <Reference Include="Microsoft.CSharp" />
  7.   <Reference Include="System.Data" />
  8.   <Reference Include="System.Xml" />
  9.   <Reference Include="System.Numerics" />
  10. </ItemGroup>
  11. <ItemGroup Condition=" '$(SilverlightBuild)' == 'true' ">
  12.   <Reference Include="mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e">
  13.     <SpecificVersion>False</SpecificVersion>
  14.     <HintPath>$(SilverlightPath)\mscorlib.dll</HintPath>
  15.   </Reference>
  16.   <Reference Include="System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e">
  17.     <SpecificVersion>False</SpecificVersion>
  18.     <HintPath>$(SilverlightPath)\System.dll</HintPath>
  19.   </Reference>
  20.   <Reference Include="System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e">
  21.     <SpecificVersion>False</SpecificVersion>
  22.     <HintPath>$(SilverlightPath)\System.Core.dll</HintPath>
  23.   </Reference>
  24.   <Reference Include="System.Numerics, Version=2.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
  25.     <SpecificVersion>False</SpecificVersion>
  26.     <HintPath>$(SilverlightSdkPath)\System.Numerics.dll</HintPath>
  27.   </Reference>
  28. </ItemGroup>

That’s all there is to it.

Testing and Verifying Our Changes

With our configuration profiles in place we can verify that everything is working properly. Simply switch to one of the Desktop build configurations and rebuild the solution. You will notice that immediately upon switching configurations the code editor window for Class1 will recognize the change in conditional symbols. Also, the Build Output window will show that the standard assemblies were referenced by csc.exe when compiling the project.

Switch to one of the Silverlight build configurations and you will again notice that the Class1 editor immediately reflects the change in conditional symbols (the other half of the conditional section becomes active). Additionally, you will notice in the Build Output window that the Silverlight assemblies were referenced by csc.exe during compilation.

So there you have it - a more robust way to handle dual-target source trees. While it requires you to exert a small amount of effort with a few manual edits of the project file, in the end it is usually less hassle than constantly updating Item Links and dealing with an IDE that never seems to load the correct file when navigating to error lines.

If you have any trouble with this, I have uploaded my example project used to write up this article here for reference:

 

Follow-up Note:

Observant readers might have noticed that this technique adds a build variable named SilverlightBuild, which is valid as a Condition for any ItemGroup, including source files themselves. If you have entire source files that need to be included/excluded under Silverlight, then you can use this functionality instead of sprinkling “#if SILVERLIGHT” throughout your source code. In the example project source I have done exactly that, and included a class named SilverlightOnlyClass that is only compiled when the build target is SilverlightDebug or SilverlightRelease.

One of the projects I am working on as of late involves hosting a DLR language (IronPython) in a managed application. Our application has dual build targets - one for WPF and one for Silverlight, with nearly identical feature sets.

When embedding IronPython in an application there are multiple things to consider from a packaging perspective. The DLR itself lives in a pair of open-source assemblies (Microsoft.Dynamic.dll and Microsoft.Scripting.dll). The IronPython runtime is also in a pair of open-source assemblies (IronPython.dll and IronPython.Modules.dll). All of these are available from the IronLanguages codeplex project in precompiled form (http://ironpython.codeplex.com/), or as source code if you prefer from the working repository at github (https://github.com/IronLanguages/main). There is support for both the desktop CLR and the Silverlight CLR.

Those assemblies provide the managed scripting runtime needed to run Python scripts inside of an application. And of course there are the python scripts themselves, which can be stored on the local file system, a web server, a database, or pretty much anyplace you can dream up. But there is a third part to this picture - and that is the Python Standard Library. Full documentation of this library can be found at http://www.python.org/. It is the Python equivalent of the .NET Framework Library, including modules for a wide range of features from xml processing to threading to math functions. This library is distributed in source code form - it is entirely written in Python. Typical applications would update the sys.path system value to point to a local directory (usually something like C:\Python27\) that contains the full suite of .py files in the standard library (or some subset). The runtime will find them when needed, and compile them just-in-time as scripts declare references to them via “import” statements.

But our application is not typical, and we wanted to exercise very tight control over that library. The last thing we want is for a user to start mucking around in C:\Python27\ and break something. So we decided to embed the entire set of Python Standard Library source files as resources. The IronPython module loader supports this technique through custom Platform Abstraction Layer classes (a topic for a different post). The problem here is that there are quite a few files in a hierarchical folder structure. Adding them all to our .csproj file manually would be cumbersome, and maintenance would be a real headache when we want to update to a newer Python library release. And so this is where the wildcard inclusions feature comes in handy.

Most developers have no idea you can do this. Visual Studio does not directly offer a way to add items as wildcards (if you specify a wildcard in the Add Files dialog, it will add individual items for each file). It actually turns out to be very easy. You need to open the csproj file in a text editor (you can do this in Visual Studio by selecting “Unload Project” from the context menu in Solution Explorer, and then “Edit [project name].csproj”).

In the csproj file you will find individual items listed in <ItemGroup> sections:

  1. <Compile Include="Parser\SquiggleTooltipProvider.cs" />

A “Compile” item indicates a source file that is compiled with the C# compiler into the target assembly. An “EmbeddedResource” item indicates a file that will be embedded as a resource in the target assembly. With 482 files in the standard library for the current release of IronPython (2.7), that is a lot of EmbeddedResource entries!

However, we can use wildcards when specifying these files. Here is an example that includes all .py files from a folder (and all subfolders):

  1. <EmbeddedResource Include="Python27\Lib\**\*.py" />

Once you save the project file and reload it in Visual Studio, you will see that the Solution Explorer understands the wildcard and will populate with all files that match it. You can copy new .py files into the target directory and Solution Explorer will update accordingly (you need to click the “Refresh” button for this). And while it shows the results of the wildcard inclusion, it does not alter the entries in the project file unlike the behavior of the Add Files dialog.

This is a nice feature of the project file specification, and it really helped with this particular situation, but there are some gotchas to be aware of. First of all, Visual Studio doesn’t provide a UI for creating or editing the wildcards - you need to do it by hand. Secondly, the Version Control hooks will simply ignore it, and you will need to add/manage the included files directly in your Source Control Manager if your code is affected by an SCM such as TFS. Lastly, you cannot combine this feature with the “link to item” feature that allows you to add an existing item as a link. Those two features are exclusive of each other. I mention this last limitation because it is common practice to support dual-target Silverlight/Desktop codebases by making heavy use of linked files. There are ways around this limitation, but that too is an entire blog post subject in itself.

In my previous article, I spoke about DynamicMethod. Today I would like to talk briefly about dynamic objects. Despite the similarity in name, these are completely different topics.

It is a simple keyword really - “dynamic”. So innocent. So effortless to type. But this new keyword hides some powerful mojo. If you happen to be working with code that integrates with the DLR (Dynamic Language Runtime), then you have likely already encountered this keyword in C#. And even if you aren’t working with the DLR you might find some good use for this new and fascinating language feature.

Among other things, C# 4.0 introduced the “dynamic” type declaration. On the surface it resembles the “var” keyword, and from the standpoint of compiler magic that is a good comparison (both var and dynamic are language features, and use of the keywords cause the compiler to do special things). However from the CLR platform perspective they are very different. To illustrate, let’s explore a simple example and inspect the resulting MSIL that gets compiled:

  1. static void Main(string[] args)
  2. {
  3.     object ObjVariable = "Hello, World!";
  4.     var VarVariable = "Hello, World!";
  5.     dynamic DynVariable = "Hello, World!";
  6. }
  1. .method private hidebysig static void  Main(string[] args) cil managed
  2. {
  3.   .entrypoint
  4.   // Code size       20 (0x14)
  5.   .maxstack  1
  6.   .locals init ([0] object ObjVariable,
  7.            [1] string VarVariable,
  8.            [2] object DynVariable)
  9.   IL_0000:  nop
  10.   IL_0001:  ldstr      "Hello, World!"
  11.   IL_0006:  stloc.0
  12.   IL_0007:  ldstr      "Hello, World!"
  13.   IL_000c:  stloc.1
  14.   IL_000d:  ldstr      "Hello, World!"
  15.   IL_0012:  stloc.2
  16.   IL_0013:  ret
  17. } // end of method Program::Main

Here we can see that for the “var” declaration, the C# compiler figured out that VarVariable would always be a string based on the fact that we assigned it a string literal. So when compiling to MSIL, the C# compiler simply replaced “var VarVariable” with “string VarVariable”. That is about all there is to the compiler magic behind the “var” keyword.

However when we look at the “dynamic” declaration, we can clearly see that something mysterious is afoot. The compiler treated our “dynamic” as “object”, even though we assigned it a literal string value. So far, this hasn’t really provided any benefit. We could have declared it as “object DynVariable” and achieved the same result. But clearly there is a difference, because otherwise the language would not have introduced a new (longer) keyword if there were not some purpose behind it.

Let us begin by exploring what we can do with a “dynamic” object versus a plain-vanilla “object”. Revisiting our code from above, let’s see if DynVariable offers any additional methods or properties. In the code editor we type “DynVariable.” and expect to see an Intelliprompt session displayed with a listing of the legitimate members that we can invoke. Instead, we see this:

image

Making sure Visual Studio isn’t broken, we take a look at the Intelliprompt session list for ObjVariable, and sure enough Intelliprompt is working just fine:

image

The reason for this is that when you declare something as “dynamic”, the compiler will no longer make any claims about the type of the actual object instance that is being referred to. It no longer “knows” the target type. And if it doesn’t know the type of an object, it certainly can’t help you by displaying a listing of that type’s members.

But just because the compiler doesn’t know the type or the members exposed by that type, doesn’t mean that we don’t either. In fact, we just assigned a string to DynVariable, so we know for a fact that we can, for example, use it in place of a normal string. And pretty much the simplest thing we can do with a normal string is assign it to a string variable:

  1. string StrVariable = DynVariable;

Innocent enough. We need to take a peek at the MSIL for this though. The compiler makes no claims about the type of DynVariable, yet it just allowed us to assign it to a string local variable. On the surface this seems to defy the rules of “strong typing” that the CLR in general, and C# in particular, are known for. So how did it pull this off?

  1. .method private hidebysig static void  Main(string[] args) cil managed
  2. {
  3.   .entrypoint
  4.   // Code size       87 (0x57)
  5.   .maxstack  3
  6.   .locals init ([0] object ObjVariable,
  7.            [1] string VarVariable,
  8.            [2] object DynVariable,
  9.            [3] string StrVariable)
  10.   IL_0000:  nop
  11.   IL_0001:  ldstr      "Hello, World!"
  12.   IL_0006:  stloc.0
  13.   IL_0007:  ldstr      "Hello, World!"
  14.   IL_000c:  stloc.1
  15.   IL_000d:  ldstr      "Hello, World!"
  16.   IL_0012:  stloc.2
  17.   IL_0013:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,string>> ConsoleApplication2.Program/'<Main>o__SiteContainer0'::'<>p__Site1'
  18.   IL_0018:  brtrue.s   IL_0040
  19.   IL_001a:  ldc.i4.0
  20.   IL_001b:  ldtoken    [mscorlib]System.String
  21.   IL_0020:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  22.   IL_0025:  ldtoken    ConsoleApplication2.Program
  23.   IL_002a:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  24.   IL_002f:  call       class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::Convert(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,
  25.                                                                                                                                                           class [mscorlib]System.Type,
  26.                                                                                                                                                           class [mscorlib]System.Type)
  27.   IL_0034:  call       class [System.Core]System.Runtime.CompilerServices.CallSite`1<!0> class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,string>>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)
  28.   IL_0039:  stsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,string>> ConsoleApplication2.Program/'<Main>o__SiteContainer0'::'<>p__Site1'
  29.   IL_003e:  br.s       IL_0040
  30.   IL_0040:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,string>> ConsoleApplication2.Program/'<Main>o__SiteContainer0'::'<>p__Site1'
  31.   IL_0045:  ldfld      !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,string>>::Target
  32.   IL_004a:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,string>> ConsoleApplication2.Program/'<Main>o__SiteContainer0'::'<>p__Site1'
  33.   IL_004f:  ldloc.2
  34.   IL_0050:  callvirt   instance !2 class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,string>::Invoke(!0,
  35.                                                                                                                                                     !1)
  36.   IL_0055:  stloc.3
  37.   IL_0056:  ret
  38. } // end of method Program::Main

Oh dear, that greatly increased the amount of code being executed!

So what is all of this CallSite funny business? Call Sites are the “compiler magic” that backs up the “dynamic” keyword in C#. The CallSite concept is not terribly difficult to explain, but it might be a little easier if we take a step back first and evolve our example just a little further. What if we add the following line of code to our example?

  1. int IntVariable = DynVariable;

Surely this cannot work. We have a string in DynVariable. And sure enough, while it in fact compiles, it throws a runtime exception (RuntimeBinderException) upon execution:

image

This exception was thrown because our program deferred the type check on our assignment to IntVariable until runtime. In fact, we could have done this ourselves too by doing the following:

  1. if (DynVariable == null || typeof(int).IsAssignableFrom(((object)DynVariable).GetType()) == false)
  2.     throw new Microsoft.CSharp.RuntimeBinder.RuntimeBinderException("Cannot implicitly convert type 'int' to 'string'.");
  3. int IntVariable = (int)DynVariable;

Here we are first verifying that the actual object contained within DynVariable is compatible with the variable named IntVariable. Since IntVariable is an integer (value type), this means it cannot be null as well.

Note that since we are also typecasting directly to an int in the last line of code, we no longer need to declare our variable as “dynamic”. We could easily change our code now to use “object” as the type.

We can logically split this code into two parts. The first part is the “if” statement that verifies type compatibility. The second part is the actual value assignment and type casting. For discussion’s sake, we can call these the “test” and the “action”. If the “test” passes, then we perform the “action”.

Now, what if our code is a slightly more complicated than just assigning a second variable. Suppose we want to do the following:

  1. object ObjCombined = DynVariable + DynVariable;

Now this is certainly an interesting bit of code. Since DynVariable is declared as dynamic, the compiler assumes nothing about the members it might support. And with this line of code, we are telling the compiler “trust me, there is a + operator that will work here”. What really makes it interesting though is that there is a + operator defined for both strings and integers (and other data types as well!).

Going back to the notion of a separate “test” and “action”, we can codify correct behavior for all of these cases fairly easily. We can find all + operators available within the current scope, and for each of these write a “test” to check for type compatibility with the operands and also an “action” which passes the left and right operands to the appropriate operator function (in a strongly-typed fashion).

Some of our test and action methods might look like this if we were to support both string and integer values:

  1. static bool CheckOperandTypeCompatibility<TOperand>(object left, object right)
  2. {
  3.     if (left == null || typeof(TOperand).IsAssignableFrom(left.GetType()) == false)
  4.         return false;
  5.     if (right == null || typeof(TOperand).IsAssignableFrom(right.GetType()) == false)
  6.         return false;
  7.     return true;
  8. }
  9.  
  10. static string ApplyOperator(string left, string right)
  11. {
  12.     return left + right;
  13. }
  14.  
  15. static int ApplyOperator(int left, int right)
  16. {
  17.     return left + right;
  18. }

And we could execute the appropriate tests and corresponding actions accordingly:

  1. if (CheckOperandTypeCompatibility<int>(DynVariable, DynVariable))
  2.     ObjCombined = ApplyOperator((int)DynVariable, (int)DynVariable);
  3. if (CheckOperandTypeCompatibility<string>(DynVariable, DynVariable))
  4.     ObjCombined = ApplyOperator((string)DynVariable, (string)DynVariable);

But this would get out of hand rather quickly. Our next step would be to wrap these test/action pairs into a list, and then use that list to drive the test and action execution:

  1. List<Tuple<Func<object, object, bool>, Action<object, object, object>>> OperatorTests
  2.     = new List<Tuple<Func<object, object, bool>, Action<object, object, object>>>();
  3.  
  4. // add our test/action pairs to OperatorTests for each operator we want to support
  5.  
  6. foreach (var test in OperatorTests)
  7.     if (test.Item1(DynVariable, DynVariable))
  8.     {
  9.         test.Item2(ObjCombined, DynVariable, DynVariable);
  10.         break;
  11.     }

We would probably use lambda expressions when populating the list of tests/actions. But that would still get fairly unwieldy in a hurry. We just learned about DynamicMethod in my previous blog topic, so it makes natural sense to generate these test/action delegate pairs using DynamicMethod objects that we create on the fly when we see a new set of types, and add that generated pair to our list. So DynamicMethod and the “dynamic” keyword are related to each other after all!

So at this point you might be wondering who in their right mind would want to write this kind of code? And just to avoid having to know what type we are working with from the outset?

And you would be right about that. The good thing is that we don’t have to write this stuff. In fact, through the above code we have more or less reinvented the core idea behind a CallSite. A real CallSite however is much smarter than ours. It can handle far more complex scenarios than simple binary operator functions. It will also create (and cache) the tests and actions on-demand, so if you pass a string one time and the runtime type binding fails, it will simply throw the exception right away if another string is passed, instead of trying to find the right member again (and failing).

Furthermore, we don’t really have to even know about CallSites in most cases. When we use the “dynamic” keyword, the C# compiler will automatically create CallSites in the compiled MSIL whenever you attempt to invoke members or pass the value around your application as seen in our first example above. All of the dirty details are hidden away from view (unless your view happens to include using ILDASM). This might all seem like the dynamic keyword violates the laws of type-safety, but once you understand the mechanics of CallSites, you will realize that in fact it does not violate them at all.

But it certainly pulls off a cool magic trick.

Many developers are unfamiliar with this very useful piece of machinery. The DynamicMethod class was introduced with framework version 2.0 and Silverlight version 3 as a way to offer a limited amount of IL generation at runtime without requiring the additional overhead that is needed when generating complete types or assemblies.

The primary use case is for performance optimization of generated or generic code (“generic” as in “general purpose”, not to be confused with the .NET feature of the same name), but there are other valid use cases as well. Typical solutions prior to DynamicMethod involved writing temporary C# source code files to the local temp folder, invoking the cs command-line compiler to create a randomly named assembly, and then loading that compiled assembly into memory. In fact, this is exactly what the XmlSerializer does and is responsible for a lot of the cold startup penalty that is associated with Web Services (Web Services - even WCF services in common configurations - use XmlSerializer and kin under the hood). Yuck!

DynamicMethod was designed to circumvent the worst parts of those scenarios because it allows you to emit IL directly into the body of a new method delegate - without the round-trip to disk and command-line compiler. DynamicMethod objects are also stored on the heap and become eligible for garbage collection (usually a good thing, but not always).

DynamicMethod is also often used to replace code that dynamically invokes reflected members. The reflection is performed once when building the dynamic method, which performs quickly on subsequent calls (the DynamicMethod body consists of MSIL code, which is passed to the CLR’s JIT compiler just like any other pre-compiled module of code). In fact, once the DynamicMethod has been created and JIT’d, the performance is nearly as good as corresponding pre-compiled code, differing mostly because of the slight additional overhead of the delegate invocation needed when calling the method.

To demonstrate, let us contemplate the following simple class which contains a very simple public method:

  1. public class MyClass
  2. {
  3.     public string HelloWorld()
  4.     {
  5.         return "Hello, World!";
  6.     }
  7. }

Let us further contemplate having a reference to an instance of MyClass:

  1. MyClass MyObject = new MyClass();

Pretend that we do not actually have pre-existing knowledge of the MyClass type, but we do know that it has a HelloWorld() method which returns a string, and we wish to invoke that method. This is a classic (admittedly contrived) example where we would be likely to use Reflection.

In fact, our first stab at this might produce code that resembles the following:

  1. BindingFlags Flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod;
  2. string Result = (string)typeof(MyClass).InvokeMember("HelloWorld", Flags, null, MyObject, null);

This will indeed work as expected. And if you only need to call the method once, this technique just might be sufficient for your needs. If so, there is no need to read further. However, let’s presume that for whatever reason we need to call this code more frequently than that. In our next try to improve performance, we might decide to try caching the reflected MethodInfo and reusing this for subsequent calls:

  1. MethodInfo HelloWorldMethod = typeof(MyClass).GetMethod("HelloWorld");
  2. string Result = (string)HelloWorldMethod.Invoke(MyObject, null);

If we hold on to that MethodInfo instance for subsequent calls, then we can save the reflection lookup. This does indeed improve performance by about 40% if you run it a million times (and who doesn’t run HelloWorld a few million times?), which actually isn’t too bad.

But what about using a DynamicMethod? After all, the entire premise of this article is that using DynamicMethod in place of “standard reflection” may improve performance. With anything there is always a tradeoff though. In this case, to use a DynamicMethod means you need to understand some core level of MSIL. Or you at least need to know how to use Reflector to inspect some code you want to model your DynamicMethod on, and set the output language to IL (or you could alternatively use the ISDASM tool). MSIL is rather verbose when compared to the higher-level languages of C#, VB, and others. So it should come as no surprise that we need a few more lines of code to reproduce the same functionality from the previous two samples:

  1. MethodInfo HelloWorldMethod = typeof(MyClass).GetMethod("HelloWorld");
  2. DynamicMethod DM = new DynamicMethod("HelloWorld", typeof(string), new Type[] { typeof(MyClass) });
  3. ILGenerator IL = DM.GetILGenerator();
  4. IL.Emit(OpCodes.Ldarg_0);
  5. IL.Emit(OpCodes.Call, HelloWorldMethod);
  6. IL.Emit(OpCodes.Ret);
  7. Func<MyClass, string> DMDelegate = (Func<MyClass, string>)DM.CreateDelegate(typeof(Func<MyClass, string>));
  8. string Result = DMDelegate(MyObject);

This time we are creating a new DynamicMethod object, emitting a few MSIL opcodes which just take an incoming function parameter (the object we are calling HelloWorld() on), invokes the method and then returns back. Interestingly, we are still using Reflection in order to get a reference to the MethodInfo that we intend to invoke within the DynamicMethod.

So how does the DynamicMethod perform in comparison with the other approaches? Again, with a test run of 1 million iterations, performance improved by 98%. Nice.

Measuring InvokeMember... 1000000 iterations in 1.5643784 seconds.
Measuring MethodInfo.Invoke... 1000000 iterations in 0.8150111 seconds.
Measuring DynamicMethod... 1000000 iterations in 0.0330202 seconds.
Measuring direct call... 1000000 iterations in 0.0136752 seconds.
Press any key to continue.

So even though DynamicMethod requires a little more effort, the performance increase can be very significant in certain scenarios. While this example might seem a bit unrealistic, keep in mind that in most cases where Reflection is used we are invoking far more than a single simple method. In the common use case of custom serializers, we often iterate over large class models, reflecting each property and storing/retrieving values in a very repetitive fashion. You don’t need a million serialization operations to see a performance boost.

For completeness I also ran the same number of iterations by directly calling the HelloWorld() method without any intermediary to measure the overhead of the DynamicMethod delegate call itself. As you can see, this overhead amounts to about half of the cost of invoking the calls via DynamicMethod. Note that this expenditure is a “one-time” cost per call though, and does not scale upwards as the size of the DynamicMethod increases. In other words, this test amplifies the difference about as much as is possible since the method we are calling is doing such little amount of work in the first place.

And just to point out once more (since many folks would assume otherwise) - this capability exists in Silverlight too. It isn’t just a “desktop thing”.

Full source code for the above sample can be found below:

Full Source Code
  1. using System;
  2. using System.Diagnostics;
  3. using System.Reflection;
  4. using System.Reflection.Emit;
  5.  
  6. namespace ConsoleApplication1
  7. {
  8.     public class Program
  9.     {
  10.         public class MyClass
  11.         {
  12.             public string HelloWorld()
  13.             {
  14.                 return "Hello, World!";
  15.             }
  16.         }
  17.  
  18.         static void TestInvokeMember(MyClass testObject, int iterations)
  19.         {
  20.             BindingFlags Flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod;
  21.             for (int ix = 0; ix < iterations; ix++)
  22.                 typeof(MyClass).InvokeMember("HelloWorld", Flags, null, testObject, null);
  23.         }
  24.  
  25.         static void TestMethodInfoInvoke(MyClass testObject, int iterations)
  26.         {
  27.             MethodInfo HelloWorldMethod = typeof(MyClass).GetMethod("HelloWorld");
  28.             for (int ix = 0; ix < iterations; ix++)
  29.                 HelloWorldMethod.Invoke(testObject, null);
  30.         }
  31.  
  32.         static void TestDynamicMethod(MyClass testObject, int iterations)
  33.         {
  34.             MethodInfo HelloWorldMethod = typeof(MyClass).GetMethod("HelloWorld");
  35.             DynamicMethod DM = new DynamicMethod("HelloWorld", typeof(string), new Type[] { typeof(MyClass) });
  36.             ILGenerator IL = DM.GetILGenerator();
  37.             IL.Emit(OpCodes.Ldarg_0);
  38.             IL.Emit(OpCodes.Call, HelloWorldMethod);
  39.             IL.Emit(OpCodes.Ret);
  40.             Func<MyClass, string> DMDelegate = (Func<MyClass, string>)DM.CreateDelegate(typeof(Func<MyClass, string>));
  41.             for (int ix = 0; ix < iterations; ix++)
  42.                 DMDelegate(testObject);
  43.         }
  44.  
  45.         static void TestDirectMethod(MyClass testObject, int iterations)
  46.         {
  47.             for (int ix = 0; ix < iterations; ix++)
  48.                 testObject.HelloWorld();
  49.         }
  50.  
  51.         static void Main(string[] args)
  52.         {
  53.             MyClass MyObject = new MyClass();
  54.             int Iter = 1000000;
  55.             Stopwatch SW = new Stopwatch();
  56.  
  57.             Console.Write("Measuring InvokeMember... ");
  58.             SW.Reset();
  59.             SW.Start();
  60.             TestInvokeMember(MyObject, Iter);
  61.             SW.Stop();
  62.             Console.WriteLine("{0} iterations in {1} seconds.", Iter, SW.Elapsed.TotalSeconds);
  63.  
  64.             Console.Write("Measuring MethodInfo.Invoke... ");
  65.             SW.Reset();
  66.             SW.Start();
  67.             TestMethodInfoInvoke(MyObject, Iter);
  68.             SW.Stop();
  69.             Console.WriteLine("{0} iterations in {1} seconds.", Iter, SW.Elapsed.TotalSeconds);
  70.  
  71.             Console.Write("Measuring DynamicMethod... ");
  72.             SW.Reset();
  73.             SW.Start();
  74.             TestDynamicMethod(MyObject, Iter);
  75.             SW.Stop();
  76.             Console.WriteLine("{0} iterations in {1} seconds.", Iter, SW.Elapsed.TotalSeconds);
  77.  
  78.             Console.Write("Measuring direct call... ");
  79.             SW.Reset();
  80.             SW.Start();
  81.             TestDirectMethod(MyObject, Iter);
  82.             SW.Stop();
  83.             Console.WriteLine("{0} iterations in {1} seconds.", Iter, SW.Elapsed.TotalSeconds);
  84.  
  85.             Console.WriteLine("Press any key to continue.");
  86.             Console.ReadKey();
  87.         }
  88.     }
  89. }