February 15, 2012 @ 2:17 pm
- from Maui, Hawaii
The HTML Help 1.0 specification aka CHM files, is pretty old. In fact, it's practically ancient as it was introduced in 1997 when Internet Explorer 4 was introduced. Html Help 1.0 is basically a completely HTML based Help system that uses a Help Viewer that internally uses Internet Explorer to render the HTML Help content. Because of its use of the Internet Explorer shell for rendering there were many security issues in the past, which resulted in locking down of the Web Browser control in Windows and also the Help Engine which caused some unfortunate side effects.
Even so, CHM continues to be a popular help format because it is very easy to produce content for it, using plain HTML and because it works with many Windows application platforms out of the box. While there have been various attempts to replace CHM help files CHM files still seem to be a popular choice for many applications to display their help systems. The biggest alternative these days is no system based help at all, but links to online documentation.
For Windows apps though it's still very common to see CHM help files and there are still a ton of CHM help out there and lots of tools (including our own West Wind Html Help Builder) that produce output for CHM files as well as Web output.
Image is Everything and you ain't got it!
One problem with the CHM engine is that it's stuck with an ancient Internet Explorer version for rendering. For example if you have help content that uses HTML5 or CSS3 content you might have an HTML Help topic like the following shown here in a full Web Browser instance of Internet Explorer:
The page clearly uses some CSS3 features like rounded corners and box shadows that are rendered using plain CSS 3 features. Note that I used Internet Explorer on purpose here to demonstrate that IE9 on Windows 7 can properly render this content using some of the new features of CSS, but the same is true for all other recent versions of the major browsers (FireFox 3.1+, Safari 4.5+, WebKit 9+ etc.).
Unfortunately if you take this nice and simple CSS3 content and run it through the HTML Help compiler to produce a CHM file the resulting output on the same machine looks a bit less flashy:
All the CSS3 styling is gone and although the page display and functionality still works, but all the extra styling features are gone. This even though I am running this on a Windows 7 machine that has IE9 that should be able to render these CSS features. Bummer.
Web Browser Control - perpetually stuck in IE 7 Mode
The problem is the Web Browser/Shell Components in Windows. This component is and has been part of Windows for as long as Internet Explorer has been around, but the Web Browser control hasn't kept up with the latest versions of IE. In a nutshell the control is stuck in IE7 rendering mode for engine compatibility reasons by default. However, there is at least one way to fix this explicitly using Registry keys on a per application basis.
The key point from that blog article is that you can override the IE rendering engine for a particular executable by setting one (or more) registry flags that tell the Windows Shell which version of the Internet Explorer rendering engine to load. An application that wishes to use a more recent version of Internet Explorer can then register itself during installation for the specific IE version desired and from then on the application will use that version of the Web Browser component. If the application is older than the specified version it falls back to the default version (IE 7 rendering).
Forcing CHM files to display with IE9 (or later) Rendering
Knowing that we can force the IE usage for a given process it's also possible to affect the CHM rendering by setting same keys on the executable that's hosting the CHM file. What that executable file is depends on the type of application as there are a number of ways that can launch the help engine.
- hh.exe
The standalone Windows CHM Help Viewer that launches when you launch a CHM from Windows Explorer. You can manually add hh.exe to the registry keys.
- YourApplication.exe
If you're using .NET or any tool that internally uses the hhControl ActiveX control to launch help content your application is your host. You should add your application's exe to the registry during application startup.
- foxhhelp9.exe
If you're building a FoxPro application that uses the built-in help features, foxhhelp9.exe is used to actually host the help controls. Make sure to add this executable to the registry.
What to set
You can configure the Internet Explorer version used for an application in the registry by specifying the executable file name and a value that specifies the IE version desired.
There are two different sets of keys for 32 bit and 64 bit applications.
32 bit only or 64 bit:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\MAIN\FeatureControl\FEATURE_BROWSER_EMULATION
Value Key: hh.exe
32 bit on 64 bit machine:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\MAIN\FeatureControl\FEATURE_BROWSER_EMULATION
Value Key: hh.exe
Note that it's best to always set both values ideally when you install your application so it works regardless of which platform you run on.
The value specified is a DWORD value and the interesting values are decimal 9000 for IE9 rendering mode depending on !DOCTYPE settings or 9999 for IE 9 standards mode always. You can use the same logic for 8000 and 8888 for IE8 and the final value of 7000 for IE7 (one has to wonder what they're going todo for version 10 to perpetuate that pattern).
I think 9000 is the value you'd most likely want to use. 9000 means that IE9 will be used for rendering but unless the right doctypes are used (XHTML and HTML5 specifically) IE will still fall back into quirks mode as needed. This should allow existing pages to continue to use the fallback engine while new pages that have the proper HTML doctype set can take advantage of the newest features.
Here's an example of how I set the registry keys in my Tarma Installmate registry configuration:
Note that I set all three values both under the Software and Wow6432Node keys so that this works regardless of where these EXEs are launched from. Even though all apps are 32 bit apps, the 64 bit (the default one shown selected) key is often used.
So, now once I've set the registry key for hh.exe I can now launch my CHM help file from Explorer and see the following CSS3 IE9 rendered display:

Summary
It sucks that we have to go through all these hoops to get what should be natural behavior for an application to support the latest features available on a system. But it shouldn't be a surprise - the Windows Help team (if there even is such a thing) has not been known for forward looking technologies. It's a pretty big hassle that we have to resort to setting registry keys in order to get the Web Browser control and the internal CHM engine to render itself properly but at least it's possible to make it work after all.
Using this technique it's possible to ship an application with a help file and allow your CHM help to display with richer CSS markup and correct rendering using the stricter and more consistent XHTML or HTML5 doctypes. If you provide both Web help and in-application help (and why not if you're building from a single source) you now can side step the issue of your customers asking: Why does my help file look so much shittier than the online help… No more!
February 08, 2012 @ 2:28 am
- from Maui, Hawaii
I love dynamic functionality in a strongly typed language because it offers us the best of both worlds. In C# (or any of the main .NET languages) we now have the dynamic type that provides a host of dynamic features for the static C# language.
One place where I've found dynamic to be incredibly useful is in building extensible types or types that expose traditionally non-object data (like dictionaries) in easier to use and more readable syntax. I wrote about a couple of these for accessing old school ADO.NET DataRows and DataReaders more easily for example. These classes are dynamic wrappers that provide easier syntax and auto-type conversions which greatly simplifies code clutter and increases clarity in existing code.
ExpandoObject in .NET 4.0
Another great use case for dynamic objects is the ability to create extensible objects - objects that start out with a set of static members and then can add additional properties and even methods dynamically. The .NET 4.0 framework actually includes an ExpandoObject class which provides a very dynamic object that allows you to add properties and methods on the fly and then access them again.
For example with ExpandoObject you can do stuff like this:
dynamic expand = new ExpandoObject();
expand.Name = "Rick";
expand.HelloWorld = (Func<string, string>) ((string name) =>
{
return "Hello " + name;
});
Console.WriteLine(expand.Name);
Console.WriteLine(expand.HelloWorld("Dufus"));
Internally ExpandoObject uses a Dictionary like structure and interface to store properties and methods and then allows you to add and access properties and methods easily. As cool as ExpandoObject is it has a few shortcomings too:
- It's a sealed type so you can't use it as a base class
- It only works off 'properties' in the internal Dictionary - you can't expose existing type data
- It doesn't serialize to XML or with DataContractSerializer/DataContractJsonSerializer
Expando - A truly extensible Object
ExpandoObject is nice if you just need a dynamic container for a dictionary like structure. However, if you want to build an extensible object that starts out with a set of strongly typed properties and then allows you to extend it, ExpandoObject does not work because it's a sealed class that can't be inherited.
I started thinking about this very scenario for one of my applications I'm building for a customer. In this system we are connecting to various different user stores. Each user store has the same basic requirements for username, password, name etc. But then each store also has a number of extended properties that is available to each application. In the real world scenario the data is loaded from the database in a data reader and the known properties are assigned from the known fields in the database. All unknown fields are then 'added' to the expando object dynamically.
In the past I've done this very thing with a separate property - Properties - just like I do for this class. But the property and dictionary syntax is not ideal and tedious to work with.
I started thinking about how to represent these extra property structures. One way certainly would be to add a Dictionary, or an ExpandoObject to hold all those extra properties. But wouldn't it be nice if the application could actually extend an existing object that looks something like this as you can with the Expando object:
public class User : Westwind.Utilities.Dynamic.Expando
{
public string Email { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public DateTime? ExpiresOn { get; set; }
}
and then simply start extending the properties of this object dynamically? Using the Expando object I describe later you can now do the following:
[TestMethod]
public void UserExampleTest()
{
var user = new User();
// Set strongly typed properties
user.Email = "rick@west-wind.com";
user.Password = "nonya123";
user.Name = "Rickochet";
user.Active = true;
// Now add dynamic properties
dynamic duser = user;
duser.Entered = DateTime.Now;
duser.Accesses = 1;
// you can also add dynamic props via indexer
user["NickName"] = "AntiSocialX";
duser["WebSite"] = "http://www.west-wind.com/weblog";
// Access strong type through dynamic ref
Assert.AreEqual(user.Name,duser.Name);
// Access strong type through indexer
Assert.AreEqual(user.Password,user["Password"]);
// access dyanmically added value through indexer
Assert.AreEqual(duser.Entered,user["Entered"]);
// access index added value through dynamic
Assert.AreEqual(user["NickName"],duser.NickName);
// loop through all properties dynamic AND strong type properties (true)
foreach (var prop in user.GetProperties(true))
{
object val = prop.Value;
if (val == null)
val = "null";
Console.WriteLine(prop.Key + ": " + val.ToString());
}
}
As you can see this code somewhat blurs the line between a static and dynamic type. You start with a strongly typed object that has a fixed set of properties. You can then cast the object to dynamic (as I discussed in my last post) and add additional properties to the object. You can also use an indexer to add dynamic properties to the object.
To access the strongly typed properties you can use either the strongly typed instance, the indexer or the dynamic cast of the object. Personally I think it's kinda cool to have an easy way to access strongly typed properties by string which can make some data scenarios much easier.
To access the 'dynamically added' properties you can use either the indexer on the strongly typed object, or property syntax on the dynamic cast.
Using the dynamic type allows all three modes to work on both strongly typed and dynamic properties.
Finally you can iterate over all properties, both dynamic and strongly typed if you chose. Lots of flexibility.
Note also that by default the Expando object works against the (this) instance meaning it extends the current object. You can also pass in a separate instance to the constructor in which case that object will be used to iterate over to find properties rather than this.
Using this approach provides some really interesting functionality when use the dynamic type. To use this we have to add an explicit constructor to the Expando subclass:
public class User : Westwind.Utilities.Dynamic.Expando
{
public string Email { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public DateTime? ExpiresOn { get; set; }
public User() : base()
{ }
// only required if you want to mix in seperate instance
public User(object instance)
: base(instance)
{
}
}
to allow the instance to be passed. When you do you can now do:
[TestMethod]
public void ExpandoMixinTest()
{
// have Expando work on Addresses
var user = new User( new Address() );
// cast to dynamicAccessToPropertyTest
dynamic duser = user;
// Set strongly typed properties
duser.Email = "rick@west-wind.com";
user.Password = "nonya123";
// Set properties on address object
duser.Address = "32 Kaiea";
//duser.Phone = "808-123-2131";
// set dynamic properties
duser.NonExistantProperty = "This works too";
// shows default value Address.Phone value
Console.WriteLine(duser.Phone);
}
Using the dynamic cast in this case allows you to access *three* different 'objects': The strong type properties, the dynamically added properties in the dictionary and the properties of the instance passed in! Effectively this gives you a way to simulate multiple inheritance (which is scary - so be very careful with this, but you can do it).
How Expando works
Behind the scenes Expando is a DynamicObject subclass as I discussed in my last post. By implementing a few of DynamicObject's methods you can basically create a type that can trap 'property missing' and 'method missing' operations. When you access a non-existant property a known method is fired that our code can intercept and provide a value for. Internally Expando uses a custom dictionary implementation to hold the dynamic properties you might add to your expandable object.
Let's look at code first. The code for the Expando type is straight forward and given what it provides relatively short. Here it is.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Dynamic;
using System.Reflection;
namespace Westwind.Utilities.Dynamic
{
/// <summary>
/// Class that provides extensible properties and methods. This
/// dynamic object stores 'extra' properties in a dictionary or
/// checks the actual properties of the instance.
///
/// This means you can subclass this expando and retrieve either
/// native properties or properties from values in the dictionary.
///
/// This type allows you three ways to access its properties:
///
/// Directly: any explicitly declared properties are accessible
/// Dynamic: dynamic cast allows access to dictionary and native properties/methods
/// Dictionary: Any of the extended properties are accessible via IDictionary interface
/// </summary>
[Serializable]
public class Expando : DynamicObject, IDynamicMetaObjectProvider
{
/// <summary>
/// Instance of object passed in
/// </summary>
object Instance;
/// <summary>
/// Cached type of the instance
/// </summary>
Type InstanceType;
PropertyInfo[] InstancePropertyInfo
{
get
{
if (_InstancePropertyInfo == null && Instance != null)
_InstancePropertyInfo = Instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
return _InstancePropertyInfo;
}
}
PropertyInfo[] _InstancePropertyInfo;
/// <summary>
/// String Dictionary that contains the extra dynamic values
/// stored on this object/instance
/// </summary>
/// <remarks>Using PropertyBag to support XML Serialization of the dictionary</remarks>
public PropertyBag Properties = new PropertyBag();
//public Dictionary<string,object> Properties = new Dictionary<string, object>();
/// <summary>
/// This constructor just works off the internal dictionary and any
/// public properties of this object.
///
/// Note you can subclass Expando.
/// </summary>
public Expando()
{
Initialize(this);
}
/// <summary>
/// Allows passing in an existing instance variable to 'extend'.
/// </summary>
/// <remarks>
/// You can pass in null here if you don't want to
/// check native properties and only check the Dictionary!
/// </remarks>
/// <param name="instance"></param>
public Expando(object instance)
{
Initialize(instance);
}
protected virtual void Initialize(object instance)
{
Instance = instance;
if (instance != null)
InstanceType = instance.GetType();
}
/// <summary>
/// Try to retrieve a member by name first from instance properties
/// followed by the collection entries.
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
// first check the Properties collection for member
if (Properties.Keys.Contains(binder.Name))
{
result = Properties[binder.Name];
return true;
}
// Next check for Public properties via Reflection
if (Instance != null)
{
try
{
return GetProperty(Instance, binder.Name, out result);
}
catch { }
}
// failed to retrieve a property
result = null;
return false;
}
/// <summary>
/// Property setter implementation tries to retrieve value from instance
/// first then into this object
/// </summary>
/// <param name="binder"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
// first check to see if there's a native property to set
if (Instance != null)
{
try
{
bool result = SetProperty(Instance, binder.Name, value);
if (result)
return true;
}
catch { }
}
// no match - set or add to dictionary
Properties[binder.Name] = value;
return true;
}
/// <summary>
/// Dynamic invocation method. Currently allows only for Reflection based
/// operation (no ability to add methods dynamically).
/// </summary>
/// <param name="binder"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (Instance != null)
{
try
{
// check instance passed in for methods to invoke
if (InvokeMethod(Instance, binder.Name, args, out result))
return true;
}
catch { }
}
result = null;
return false;
}
/// <summary>
/// Reflection Helper method to retrieve a property
/// </summary>
/// <param name="instance"></param>
/// <param name="name"></param>
/// <param name="result"></param>
/// <returns></returns>
protected bool GetProperty(object instance, string name, out object result)
{
if (instance == null)
instance = this;
var miArray = InstanceType.GetMember(name, BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Instance);
if (miArray != null && miArray.Length > 0)
{
var mi = miArray[0];
if (mi.MemberType == MemberTypes.Property)
{
result = ((PropertyInfo)mi).GetValue(instance,null);
return true;
}
}
result = null;
return false;
}
/// <summary>
/// Reflection helper method to set a property value
/// </summary>
/// <param name="instance"></param>
/// <param name="name"></param>
/// <param name="value"></param>
/// <returns></returns>
protected bool SetProperty(object instance, string name, object value)
{
if (instance == null)
instance = this;
var miArray = InstanceType.GetMember(name, BindingFlags.Public | BindingFlags.SetProperty |
BindingFlags.Instance);
if (miArray != null && miArray.Length > 0)
{
var mi = miArray[0];
if (mi.MemberType == MemberTypes.Property)
{
((PropertyInfo)mi).SetValue(Instance, value, null);
return true;
}
}
return false;
}
/// <summary>
/// Reflection helper method to invoke a method
/// </summary>
/// <param name="instance"></param>
/// <param name="name"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
protected bool InvokeMethod(object instance, string name, object[] args, out object result)
{
if (instance == null)
instance = this;
// Look at the instanceType
var miArray = InstanceType.GetMember(name,
BindingFlags.InvokeMethod |
BindingFlags.Public | BindingFlags.Instance);
if (miArray != null && miArray.Length > 0)
{
var mi = miArray[0] as MethodInfo;
result = mi.Invoke(Instance, args);
return true;
}
result = null;
return false;
}
/// <summary>
/// Convenience method that provides a string Indexer
/// to the Properties collection AND the strongly typed
/// properties of the object by name.
///
/// // dynamic
/// exp["Address"] = "112 nowhere lane";
/// // strong
/// var name = exp["StronglyTypedProperty"] as string;
/// </summary>
/// <remarks>
/// The getter checks the Properties dictionary first
/// then looks in PropertyInfo for properties.
/// The setter checks the instance properties before
/// checking the Properties dictionary.
/// </remarks>
/// <param name="key"></param>
///
/// <returns></returns>
public object this[string key]
{
get
{
try
{
// try to get from properties collection first
return Properties[key];
}
catch (KeyNotFoundException ex)
{
// try reflection on instanceType
object result = null;
if (GetProperty(Instance, key, out result))
return result;
// nope doesn't exist
throw;
}
}
set
{
if (Properties.ContainsKey(key))
{
Properties[key] = value;
return;
}
// check instance for existance of type first
var miArray = InstanceType.GetMember(key, BindingFlags.Public | BindingFlags.GetProperty);
if (miArray != null && miArray.Length > 0)
SetProperty(Instance, key, value);
else
Properties[key] = value;
}
}
/// <summary>
/// Returns and the properties of
/// </summary>
/// <param name="includeProperties"></param>
/// <returns></returns>
public IEnumerable<KeyValuePair<string,object>> GetProperties(bool includeInstanceProperties = false)
{
if (includeInstanceProperties && Instance != null)
{
foreach (var prop in this.InstancePropertyInfo)
yield return new KeyValuePair<string, object>(prop.Name, prop.GetValue(Instance, null));
}
foreach (var key in this.Properties.Keys)
yield return new KeyValuePair<string, object>(key, this.Properties[key]);
}
/// <summary>
/// Checks whether a property exists in the Property collection
/// or as a property on the instance
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool Contains(KeyValuePair<string, object> item, bool includeInstanceProperties = false)
{
bool res = Properties.ContainsKey(item.Key);
if (res)
return true;
if (includeInstanceProperties && Instance != null)
{
foreach (var prop in this.InstancePropertyInfo)
{
if (prop.Name == item.Key)
return true;
}
}
return false;
}
}
}
Although the Expando class supports an indexer, it doesn't actually implement IDictionary or even IEnumerable. It only provides the indexer and Contains() and GetProperties() methods, that work against the Properties dictionary AND the internal instance.
The reason for not implementing IDictionary is that a) it doesn't add much value since you can access the Properties dictionary directly and that b) I wanted to keep the interface to class very lean so that it can serve as an entity type if desired. Implementing these IDictionary (or even IEnumerable) causes LINQ extension methods to pop up on the type which obscures the property interface and would only confuse the purpose of the type. IDictionary and IEnumerable are also problematic for XML and JSON Serialization - the XML Serializer doesn't serialize IDictionary<string,object>, nor does the DataContractSerializer. The JavaScriptSerializer does serialize, but it treats the entire object like a dictionary and doesn't serialize the strongly typed properties of the type, only the dictionary values which is also not desirable. Hence the decision to stick with only implementing the indexer to support the user["CustomProperty"] functionality and leaving iteration functions to the publicly exposed Properties dictionary.
Note that the Dictionary used here is a custom PropertyBag class I created to allow for serialization to work. One important aspect for my apps is that whatever custom properties get added they have to be accessible to AJAX clients since the particular app I'm working on is a SIngle Page Web app where most of the Web access is through JSON AJAX calls. PropertyBag can serialize to XML and one way serialize to JSON using the JavaScript serializer (not the DCS serializers though).
The key components that make Expando work in this code are the Properties Dictionary and the TryGetMember() and TrySetMember() methods. The Properties collection is public so if you choose you can explicitly access the collection to get better performance or to manipulate the members in internal code (like loading up dynamic values form a database).
Notice that TryGetMember() and TrySetMember() both work against the dictionary AND the internal instance to retrieve and set properties. This means that user["Name"] works against native properties of the object as does user["Name"] = "RogaDugDog".
What's your Use Case?
This is still an early prototype but I've plugged it into one of my customer's applications and so far it's working very well. The key features for me were the ability to easily extend the type with values coming from a database and exposing those values in a nice and easy to use manner. I'm also finding that using this type of object for ViewModels works very well to add custom properties to view models. I suspect there will be lots of uses for this - I've been using the extra dictionary approach to extensibility for years - using a dynamic type to make the syntax cleaner is just a bonus here.
What can you think of to use this for?
Resources
February 01, 2012 @ 2:47 am
- from Maui, Hawaii
I've been working a bit with C# custom dynamic types for several customers recently and I've seen some confusion in understanding how dynamic types are referenced. This discussion specifically centers around types that implement IDynamicMetaObjectProvider or subclass from DynamicObject as opposed to arbitrary type casts of standard .NET types. IDynamicMetaObjectProvider types are treated special when they are cast to the dynamic type.
Assume for a second that I've created my own implementation of a custom dynamic type called DynamicFoo which is about as simple of a dynamic class that I can think of:
public class DynamicFoo : DynamicObject
{
Dictionary<string, object> properties = new Dictionary<string, object>();
public string Bar { get; set; }
public DateTime Entered { get; set; }
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
if (!properties.ContainsKey(binder.Name))
return false;
result = properties[binder.Name];
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
properties[binder.Name] = value;
return true;
}
}
This class has an internal dictionary member and I'm exposing this dictionary member through a dynamic by implementing DynamicObject. This implementation exposes the properties dictionary so the dictionary keys can be referenced like properties (foo.NewProperty = "Cool!"). I override TryGetMember() and TrySetMember() which are fired at runtime every time you access a 'property' on a dynamic instance of this DynamicFoo type.
Strong Typing and Dynamic Casting
I now can instantiate and use DynamicFoo in a couple of different ways:
Strong Typing
DynamicFoo fooExplicit = new DynamicFoo();
var fooVar = new DynamicFoo();
These two commands are essentially identical and use strong typing. The compiler generates identical code for both of them. The var statement is merely a compiler directive to infer the type of fooVar at compile time and so the type of fooExplicit is DynamicFoo, just like fooExplicit. This is very static - nothing dynamic about it - and it completely ignores the IDynamicMetaObjectProvider implementation of my class above as it's never used.
Using either of these I can access the native properties:
DynamicFoo fooExplicit = new DynamicFoo();
// static typing assignments
fooVar.Bar = "Barred!";
fooExplicit.Entered = DateTime.Now;
// echo back static values
Console.WriteLine(fooVar.Bar);
Console.WriteLine(fooExplicit.Entered);
but I have no access whatsoever to the properties dictionary. Basically this creates a strongly typed instance of the type with access only to the strongly typed interface. You get no dynamic behavior at all. The IDynamicMetaObjectProvider features don't kick in until you cast the type to dynamic.
If I try to access a non-existing property on fooExplicit I get a compilation error that tells me that the property doesn't exist. Again, it's clearly and utterly non-dynamic.
Dynamic
dynamic fooDynamic = new DynamicFoo();
fooDynamic on the other hand is created as a dynamic type and it's a completely different beast. I can also create a dynamic by simply casting any type to dynamic like this:
DynamicFoo fooExplicit = new DynamicFoo();
dynamic fooDynamic = fooExplicit;
Note that dynamic typically doesn't require an explicit cast as the compiler automatically performs the cast so there's no need to use as dynamic.
Dynamic functionality works at runtime and allows for the dynamic wrapper to look up and call members dynamically. A dynamic type will look for members to access or call in two places:
- Using the strongly typed members of the object
- Using theIDynamicMetaObjectProvider Interface methods to access members
So rather than statically linking and calling a method or retrieving a property, the dynamic type looks up - at runtime - where the value actually comes from. It's essentially late-binding which allows runtime determination what action to take when a member is accessed at runtime *if* the member you are accessing does not exist on the object. Class members are checked first before IDynamicMetaObjectProvider interface methods are kick in.
All of the following works with the dynamic type:
dynamic fooDynamic = new DynamicFoo();
// dynamic typing assignments
fooDynamic.NewProperty = "Something new!";
fooDynamic.LastAccess = DateTime.Now;
// dynamic assigning static properties
fooDynamic.Bar = "dynamic barred";
fooDynamic.Entered = DateTime.Now;
// echo back dynamic values
Console.WriteLine(fooDynamic.NewProperty);
Console.WriteLine(fooDynamic.LastAccess);
Console.WriteLine(fooDynamic.Bar);
Console.WriteLine(fooDynamic.Entered);
The dynamic type can access the native class properties (Bar and Entered) and create and read new ones (NewProperty,LastAccess) all using a single type instance which is pretty cool. As you can see it's pretty easy to create an extensible type this way that can dynamically add members at runtime dynamically.
The Alter Ego of IDynamicObject
The key point here is that all three statements - explicit, var and dynamic - declare a new DynamicFoo(), but the dynamic declaration results in completely different behavior than the first two simply because the type has been cast to dynamic.
Dynamic binding means that the type loses its typical strong typing, compile time features. You can see this easily in the Visual Studio code editor. As soon as you assign a value to a dynamic you lose Intellisense and you see
which means there's no Intellisense and no compiler type checking on any members you apply to this instance.
If you're new to the dynamic type it might seem really confusing that a single type can behave differently depending on how it is cast, but that's exactly what happens when you use a type that implements IDynamicMetaObjectProvider. Declare the type as its strong type name and you only get to access the native instance members of the type. Declare or cast it to dynamic and you get dynamic behavior which accesses native members plus it uses IDynamicMetaObjectProvider implementation to handle any missing member definitions by running custom code.
You can easily cast objects back and forth between dynamic and the original type:
dynamic fooDynamic = new DynamicFoo();
fooDynamic.NewProperty = "New Property Value";
DynamicFoo foo = fooDynamic;
foo.Bar = "Barred";
Here the code starts out with a dynamic cast and a dynamic assignment. The code then casts back the value to the DynamicFoo. Notice that when casting from dynamic to DynamicFoo and back we typically do not have to specify the cast explicitly - the compiler can induce the type so I don't need to specify as dynamic or as DynamicFoo.
Moral of the Story
This easy interchange between dynamic and the underlying type is actually super useful, because it allows you to create extensible objects that can expose non-member data stores and expose them as an object interface. You can create an object that hosts a number of strongly typed properties and then cast the object to dynamic and add additional dynamic properties to the same type at runtime. You can easily switch back and forth between the strongly typed instance to access the well-known strongly typed properties and to dynamic for the dynamic properties added at runtime.
Keep in mind that dynamic object access has quite a bit of overhead and is definitely slower than strongly typed binding, so if you're accessing the strongly typed parts of your objects you definitely want to use a strongly typed reference. Reserve dynamic for the dynamic members to optimize your code.
The real beauty of dynamic is that with very little effort you can build expandable objects or objects that expose different data stores to an object interface. I'll have more on this in my next post when I create a customized and extensible Expando object based on DynamicObject.
January 13, 2012 @ 1:25 pm
- from Maui, Hawaii
If you've ever run into the Unable to cast Transparent Proxy to
error, you know how frustrating it can be. Usually this is caused by multiple assemblies causing unexpected load behaviors. Here's what the problem is and how you can check for problems.Read more...
January 11, 2012 @ 12:13 am
- from Maui, Hawaii
Opening CHM files from a non-local location is no longer supported. Conveniently there's no decent error information displayed for this error so your first thought goes that there's something wrong with your help file. Here's an overview of the issue and some solutions on how to deal with it.
Read more...
January 03, 2012 @ 6:44 pm
- from Maui, Hawaii
Looks like IE9 has a bug that won't render box-shadow CSS when the box-shadow is contained within a table that has border-collapse set. Here's what the problem is and how to work around it.
Read more...
January 02, 2012 @ 2:36 am
- from Maui, Hawaii
If you've ever tried to generate an XML document from content that contains lower ASCII characters you might have found out that this will throw exceptions. Here's why this happens and how you can work around the issue in a pinch.
Read more...
December 23, 2011 @ 4:19 am
- from Maui, Hawaii
The default WebForms templates in Visual Studio still use the XHTML doctype headers by default. HTML5 doctype headers are easier to use and read and with HTML5 support now becoming mainstream and backward compatible with older browsers its time to switch those doctype headers. This post demonstrates how to change the default VS templates or create new templates altogether.
With HTML becoming more prominent and the new headers being easier to read and smaller in size, it's
Read more...
December 15, 2011 @ 2:19 am
- from Maui, Hawaii
If you're running the full version of IIS and you try to debug your Web application's startup code in Application_Start you might have found that you can't debug this code as the debugger doesn't break there. Here's why and some easy ways you can work around this limitation.
Read more...
December 10, 2011 @ 9:13 pm
- from Maui, Hawaii
The HTML 5 input controls enhancements seem like a nice feature - until you look a little closer and realize that that validation and styling these control enhancement use are likely going to interfere with your existing application logic and styling. Here are are some thoughts on the subject.
Read more...
December 10, 2011 @ 8:24 pm
- from Maui, Hawaii
HTML5 input types are new, and as it turns out ASP.NET Webforms input controls can easily create HTML5 input elements.
Read more...
December 08, 2011 @ 3:47 am
- from Maui, Hawaii
Handling keyboard input events in JavaScript can be tricky when you need to deal with key codes. There are browser difference and different behaviors for various key events. Here's a refresher on how keyboard events work and a utility that lets you test key strokes and their resulting key codes in the various events available.
Read more...