The dynamic type has been added to C# since C# 4.0 (.NET 4.5) and its main purpose is to bypass the static type checks and add more flexibility to the language. In this article, we are going to go through the basic concepts of dynamic type, and learn how it works and what we can do with it.
Let’s start.
What is the C# Dynamic Type?
C# is a statically typed language. So is dynamic type in C#? What is it for?
The dynamic type has been added to C# since version 4 because of the need to improve interoperability with COM (Component Object Model) and other dynamic languages. While that can be achieved with reflection, the dynamic type provides a natural and more intuitive way to implement the same code.
Dynamic type in C# is instantiated exclusively by using the dynamic
keyword and it can be assigned any other type.
Here is an example of two dynamic variables, one that is instantiated as an int
, and another that is a complex type (Book).
dynamic intNumber = 12; dynamic book = new Book(isbn);
Simple enough.
How Does Dynamic Type in C# Work?
Essentially, when you declare a variable dynamic
, you are telling the compiler to turn off the compile-time type checks. Dynamic is basically System.Object
type under the hood, but it doesn’t need to explicitly cast a value before using it. More on that a bit later in the article.
For now, let’s see how dynamic type works on some examples.
We can start by creating a dynamic variable:
dynamic testVariable = 5; Console.WriteLine(testVariable.GetType());
Can you guess the output of the Console.Writeline()
? It’s not hard to guess – it is of course System.Int32
.
So what happens if we change the value of the testVariable
to something entirely different like “Hello World”:
testVariable = "Hello World"; Console.WriteLine(testVariable.GetType());
Now we get the output System.String
.
But if we wish to do something like incrementing a value now, we’ll get an exception:
Console.WriteLine(testVariable++);
The code will compile, but we are going to get an exception when we run the application:
RuntimeBinderException
is the exception you will probably see the most when you work with dynamic type. As the exception message states, we cannot use the ++ operator on the variable of the type string
. So that means that the dynamic type behaves like the last type we’ve assigned to it.
One more example to wrap it up.
We have a class that gets us the instance of some logger implementation:
public class Logger { public void LogInfo(string message) { Console.WriteLine($"INFO: {message}"); } public void LogWarning(string message) { Console.WriteLine($"WARNING: {message}"); } public void LogError(string message) { Console.WriteLine($"ERROR: {message}"); } } public class LoggerFactory { public static Logger GetLogger() { return new Logger(); } } dynamic logger = LoggerFactory.GetLogger(); logger.LogInfo("Hi"); logger.LogWarning("You are about to enter a time warp"); logger.LogError("System is malfunctioning"); logger.LogTrace("Communication lost");
As far as the compiler is concerned, everything is fine. But once again we get the RuntimeBinderException
because LogTrace
method is not defined.
Okay, so that’s how the dynamic type in C# works. But what are some common use cases for dynamic type?
Should We Use the Dynamic Type in C#?
First of all, let’s make it clear that dynamic is not a silver bullet. We shouldn’t use it just because we can.
While it has its benefits, dynamic objects are harder to work with while writing code, since we don’t have an Intellisense for them due to the nature of dynamic type. On the other hand, if we have a need to implement dynamic type everywhere, we are probably using the wrong type of language. Dynamic languages are better suited for those kinds of cases.
So what are the common cases to apply a dynamic type to:
- Communicating with other dynamic languages
- Simplifying responses from API calls when we don’t know what type of object to expect (or we don’t care)
- Creating libraries that can be used between languages
- Making generic solutions when speed isn’t the main concern
- Replacing and simplifying reflection code
- More?
Why we shouldn’t use dynamic all the time because:
- It’s slower than statically typed code
- Increases the chance to get runtime exceptions
- Decreases code readability in some cases, and working with it is a bit harder due to the lack of IntelliSense
Ok, so let’s see some concrete examples of dynamic
in action.
Dynamic vs Reflection
Reflection is a mechanism to get a type of abstract System.Object
object, and to invoke its members without knowing what the concrete type of that object is.
For example, let’s see what a typical reflection flow looks like:
EmployeeFactory employeeFactory = GetEmployeeFactory(); object firstEmployee = employeeFactory.GetFirstEmployee(); Type firstEmployeeType = firstEmployee.GetType(); object workStatusObject = firstEmployeeType.InvokeMember("GetWorkStatus", BindingFlags.InvokeMethod, null, firstEmployee, null); var workStatus = Enum.Parse<WorkStatus>(workStatusObject.ToString()); Console.WriteLine(workStatus);
This piece of code although very complicated and large for its purpose retrieves an employee’s work status. All that code just to call one method.
That’s because our EmployeeFactory class returns an employee as a generic System.Object
type. System.Object
is the base class of all other classes and we can always use it when we don’t know what type of object to expect.
To call the method of an object of the type System.Object
we need to find out what type of object it is. We do that by calling the object.GetType()
method. After that, we can use the InvokeMember()
method to explicitly call GetWorkStatus method, on the firstEmployee
object.
After a bit of enum parsing, we can finally find out the work status of the firstEmployee
.
Now let’s see what the same example looks like by using dynamic:
EmployeeFactory employeeFactory = GetEmployeeFactory(); dynamic firstEmployee = employeeFactory.GetFirstEmployee(); WorkStatus workStatus = firstEmployee.GetWorkStatus(); Console.WriteLine(workStatus);
Well, that looks much simpler and easier to read. Moreover, we didn’t have to think about types or type method names as strings.
So, in the battle of dynamic vs reflection we get:
- Cleaner and more readable code
- Better performance due to dynamic type caching
- Not having hardcoded strings in our code
- Easier implementation
Once again, a small disclaimer. This example is for demonstration purposes only. Generally, both dynamic and reflection should be used sparingly and not just “because we can”.
Both dynamic and reflection can degrade the performance of an application drastically, and introduce bugs we could have avoided by implementing static mechanisms and abstractions correctly.
Var vs Object vs Dynamic
If you are new to the C# world, you might still have some problems discerning these three keywords.
Let’s break them down.
Var Keyword
Var is used in a method scope to implicitly type a variable. The variable is still strongly typed, but the compiler decides which type it will be in the runtime. We can even see the type by hovering over the var keyword.
var imNumber = 10; //implicitly typed variable int exNumber = 10; //explicitly typed variable
Our opinion is that var improves the readability of the code and it should be used because more often than not we don’t need to explicitly type our variables.
Especially if the types are complicated.
For example Dictionary<string, IEnumerable<Employee>>
or something even more complicated. The type of the variable cannot be changed at runtime.
There are pros and cons to using var, but that’s beyond the scope of this article. If you are interested in learning more about when to use it and when not, check out this blog post: When to Use and Not Use var in C#.
Object Keyword
The object is an alias for System.Object
which is the ultimate base class in C#. Every class in C# is derived from System.Object
, and we can utilize that fact to make a more generic codebase. Through the object type, we can get the concrete type of the object we are working with, in runtime. While we can achieve pretty much anything in terms of abstraction with System.Object
, sometimes we are forced to use reflection to achieve the goal we want, which is not ideal.
To be able to use the type that is assigned to the System.Object
variable, we need to explicitly cast it first. For example:
object testObj = 10; testObj = (int)testObj + 10;
Dynamic Keyword
Dynamic is actually an object type but it bypasses compiler type checks. We can assign anything to a dynamic variable and not worry about the compiler or the type. It utilizes the power of DLR which is an extension of CLR.
Although bypassing compiler checks is nice, it could potentially lead to unexpected behavior if not used carefully. We don’t need to explicitly cast dynamic
before trying out different operations.
dynamic testDynamic = 10; testDynamic = testDynamic + 25;
RuntimeBinderException
RuntimeBinderException
is the exception we get at execution time if a dynamic type fails to resolve the operation we provided on a type that doesn’t support it.
To put it in simple terms, imagine you are trying to do something on a static type and that static type doesn’t implement that behavior.
Like trying to increment a string. Or calling the String.Split()
on an integer.
It’s not that simple though, there are some gotchas that we should be aware of. For example, calling the .Count()
method on a dynamic object that has been assigned ICollection
:
public static void ExamplePrintCount(ICollection collection) { dynamic d = collection; Console.WriteLine("Static typing: {0}", collection.Count); Console.WriteLine("Dynamic typing: {0}", d.Count); }
If we run this example by providing an int array:
ExamplePrintCount(new int[20]);
What do you think the result will be?
If you guessed that first Console.WriteLine()
will output 2, and that we’ll get RuntimeBinderException
in the second one, you guessed it right.
Static typing: 20 Unhandled exception. Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'System.Array' does not contain a definition for 'Count' at CallSite.Target(Closure , CallSite , Object ) at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0) at Program.<<Main>$>g__ExamplePrintCount|0_0(ICollection collection) in D:\blog\SOURCE\ConsoleApp3\ConsoleApp3\Program.cs:line 13 at Program.<Main>$(String[] args) in D:\blog\SOURCE\ConsoleApp3\ConsoleApp3\Program.cs:line 11
This example is taken from chapter 14 of C# in Depth book which you can find here. We recommend you read that chapter if you are serious about using dynamic
in your applications. It will definitely save you some time, reduce headaches and prolong your lifespan.
Conclusion
The dynamic type is a useful construct. It has a variety of usages and it can both help us or make our life tougher if we are not careful when and how we implement it.
While dynamic has originated from the need of overcoming interoperability problems, there are a lot of nice examples you can find today where dynamic type is utilized in the best possible way. Take for example DalSoft.RestClient we’ve already written about. It is a full-blown dynamic library for consuming restful APIs from C#.
Also, it’s worth mentioning that this is not all that dynamic feature can do for us. There are more useful constructs like ExpandoObject and DynamicObject which we are going to cover in some future articles.
Does .Net core fully support the DLR (call-site caching in dynamics, etc.), or only the full framework?