The dynamic keyword was added to C# in .NET 4.0. It was probably primarily added to .NET to support interop with languages such as IronPython, also to aid in interop with COM objects and for handling anonymous types.
What’s a dynamic variable ?
A dynamic variable is not statically typed and can be assigned any type (just like an object) however we can write code that interacts with the type even though we might not know what the type looks like – in other words it’s late bound. What this really means is that we can interact methods, properties etc. even though we might not have the actual type to compile against. Our code will compile correctly even without the known type, due to the dynamic keyword but errors may occur at runtime if the actual type does not have the expected methods, properties etc. in which case we’ll get a RuntimeBinderException exception.
So to make things a little clearer, let’s assume that we have a factory class which creates an object. We don’t know the actual object type but we do know it has a FirstName property – here’s a contrived example of what the code might look like…
public static class Factory { public static object Create() { return new Person{FirstName = "Edmund", LastName = "Blackadder"}; } }
Now, obviously if we knew the type returned was a Person type, we could cast the return to type Person. But for the sake of argument we’ll assume we do not know what’s being returned, all we know is that it has a FirstName property. So we could create a dynamic variable thus
dynamic p = Factory.Create(); Console.WriteLine(p.FirstName);
Notice, we interact with the FirstName property even though we do not know the type that the dynamic variable represents. As stated, at compile time this works just fine and at runtime this will output the string “Edmund”. However, as also stated previously, say we made a spelling mistake and FirstName became FrstName, this would also compile but at runtime would fail with the RuntimeBinderException exception.
We can also store anonymous types (as mentioned at the start of this post) in a dynamic variable and interact with those type’s properties and methods, for example
dynamic p = new {FirstName="Edmund"}; Console.WriteLine(d.FirstName);
Dynamic dynamic variables
We can also define dynamic behaviour at runtime by defining our own dynamic type. So creating dynamic dynamic types (if you like).
For example, one of the cool features of the CsvProvider in F# is that if we have a CSV file with the first row storing the column headings, when we enumerate over the returned rows of data the object we get back will automatically have properties named after the headings with the row data assigned to them. Note: we cannot, as far as I currently know replicate the F# ability to add to intellisense, but let’s see how we might implement similarly late bound column name properties to a type.
So let’s assume we had the following stock data in a CSV file
Symbol,High,Low,Open,Close
MSFT,37.60,37.30,37.35,37.40
GOOG,1190,1181.38,1189,1188
etc.
We might like to read each line from a CsvReader class, maybe it has a ReadLine() method that returns a CsvData object which can change it’s properties depending upon the heading names in the CSV file.
So to implement this CsvData class by deriving it from the DynamicObject and this will now allow us to dynamically create properties at runtime. This is similar to implementing dynamic proxies.
public class CsvData : DynamicObject { private CsvReader reader; public CsvData(CsvReader reader) { this.reader = reader; } public override bool TryGetMember(GetMemberBinder binder, out object result) { result = null; if(reader.Headers.Contains(binder.Name)) { result = reader.Headers[binder.Name]; return true; } return false; } }
So the above code assumes the CsvReader has some collection type from which we can get the headers (and check whether a header name exists).
Basically if we use the following code
CsvReader reader = new CsvReader("stocks.csv"); dynamic stock = reader.ReadLine() Console.WriteLine( String.Format("Symbol {0} High {1} Low {2}", stock.Symbol, stock.High, stock.Low));
we can access properties which are created at runtime.
Obviously if we were to change the CSVReader to read data from a different type of data, maybe something like a list of CD data, for example
Artist,Album,Genre
Alice Cooper,Billion Dollar Babies,Rock
Led Zeppelin,Houses of the Holy,Rock
we can still use the same CsvReader and CsvData code to access the new properties. In this case our dynamic variable will use properties such as cd.Artist, cd.Album, cd.Genre etc.
Dynamics in LINQ
Instead of a ReadLine method on the CsvReader, what if it implemented an IEnumerator interfaces, we could now use the CsvReader with LINQ using the following
CsvReader reader = new CsvReader("stocks.csv"); var data = from dynamic line in reader where line.Close > line.Open select line; foreach(var stock in data) { Console.WriteLine( String.Format("Symbol {0} High {1} Low {2}", stock.Symbol, stock.High, stock.Low)); }
Notice the use of the dynamic keyword before the variable line. Without this the line.Close and line.Open will cause a compile time error.
Note: the above example of course assumes that line.Close and line.Open are both numerical values