What

The “dynamic” keyword was first introduced in C# 4.0. It is used something like this:

public dynamic RunDynamics() { dynamic str = "1"; str.ThisFunctionDoesntExist(); //Will throw a runtime error but will compile just fine. str = 1; //putting an integer in here will work just fine at both compile & run time. return ""; //you can return anything in a function whose return type is dynamic. }

Using this you can even return anonymous .Net types (see the “When” section before you actually do this in production code):

public static dynamic ReturnAnonymous() { return new { Foo = "bar" }; }

In the above code note that anonymous types are internal to an assembly, this means that if you pass an anonymous object from another assembly wrapped in a “dynamic” object, it will fail at runtime giving an RuntimeBinderExeception similar to:

An unhandled exception of type ‘Microsoft.CSharp.RuntimeBinder.RuntimeBinderException’ occurred in System.Core.dll Additional information: ‘object’ does not contain a definition for ‘Foo’

Why

C# is a statically typed language, what that means is that all types have defined types, even “object” is defined as every object in C# ultimately inherits from it, in order for C# to interop with dynamic languages like (iron)python etc. a dynamic runtime was required so that types need not have a definition. E.g.:

public dynamic RunPython() { const string someInput = "Some input"; const string pythonScript = "class PythonClass:\r

" + " def printfn(self, input):\r

" + " return input"; var engine = Python.CreateEngine(); var scope = engine.CreateScope(); var operations = engine.Operations; engine.Execute(pythonScript, scope); var pythonClassObj = scope.GetVariable("PythonClass"); dynamic instance = operations.CreateInstance(pythonClassObj); return instance.printfn(someInput); }

In the real world the python script will be coming from a file of course.

This interop between statically typed .Net and a dynamic language like python is made possible by the dynamic language runtime in which the dynamic keyword also operates.



Image Courtesy: MSDN @ https://msdn.microsoft.com/en-us/library/dd233052(v=vs.110).aspx

Note that the dynamic runtime does late binding i.e. does type recognition and checks at runtime instead of at compile time.

When

The dynamic keyword is powerful indeed, it is irreplaceable when used with dynamic languages but can also be used for tricky situations while designing code where a statically typed object simply will not do.

Before you get all excited about how you no longer have to type a class again, consider the drawbacks:

1. There is no compile-time type checking, this means that unless you have 100% confidence in your unit tests (cough) you are running a risk.

2. The dynamic keyword uses more CPU cycles than your old fashioned statically typed code due to the additional runtime overhead, if performance is important to your project (it normally is) don’t use dynamic.

3. Common mistakes include returning anonymous types wrapped in the dynamic keyword in public methods. Anonymous types are specific to an assembly, returning them across assembly (via the public methods) will throw an error, even though simple testing will catch this, you now have a public method which you can use only from specific places and that’s just bad design.

4. It’s a slippery slope, inexperienced developers itching to write something new and trying their best to avoid more classes (this is not necessarily limited to the inexperienced) will start using dynamic more and more if they see it in code, usually I would do a code analysis check for dynamic / add it in code review.

Dynamic keywords are there for a purpose, but the purpose is definitely not to satiate our lethargy.

The Code

Here is the complete code I used in the post for your ctrl+c pleasure:

The console class:

class Program { static void Main(string[] args) { var runner = new DynamicUser(); Console.WriteLine("running basic dynamic code............"); runner.RunDynamics(); Console.WriteLine("running python/dynamic hybrid code............"); Console.WriteLine(runner.RunPython()); Console.WriteLine("running anonymous with dynamic return type..........."); //dynamic dynamicType = runner.ReturnAnonymous(); //uncomment this to see what happens when an anonymous type is called from another assembly wrapped in a dynamic object. dynamic dynamicType = ReturnAnonymous(); Console.WriteLine(dynamicType.Foo); Console.ReadLine(); } public static dynamic ReturnAnonymous() { return new { Foo = "bar" }; } }

The Class containing dynamic code, put this in another assembly from the console to see anonymous type behaviour across assemblies:

public class DynamicUser { public dynamic RunDynamics() { dynamic str = "1"; //str.ThisFunctionDoesntExist(); //Will throw a runtime error but will compile just fine. str = 1; //putting an integer in here will work just fine at both compile & run time. return ""; //you can return anything in a function whose return type is dynamic. } public dynamic RunPython() { const string someInput = "Some input"; const string pythonScript = "class PythonClass:\r

" + " def printfn(self, input):\r

" + " return input"; var engine = Python.CreateEngine(); var scope = engine.CreateScope(); var operations = engine.Operations; engine.Execute(pythonScript, scope); var pythonClassObj = scope.GetVariable("PythonClass"); dynamic instance = operations.CreateInstance(pythonClassObj); return instance.printfn(someInput); } public object ReturnAnonymous() //calling this will return an exception from another assembly. { return new { Foo = "bar" }; } }