Best use of Dynamic in C#

Best use of Dynamic in C#

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, learn how it works and what we can do with it.

So here’s exactly what we are going to learn:

  • Static vs Dynamic Languages
  • Advantages of Static and Dynamic Languages
  • What is Dynamic Type in C#
  • How Does Dynamic Work?
  • Why Should We Use the Dynamic Type
  • Dynamic vs Reflection
  • A Short Overview of DLRExpression Trees
  • Call Site Caching
  • Dynamic Object Interoperability
  • Var vs Object vs Dynamic Var Keyword
  • Object Keyword
  • Dynamic Keyword
  • RuntimeBinderException
  • Conclusion

Let’s start.

Static vs Dynamic Languages

As you might be aware, the software development languages are divided into two major categories: static languages and dynamic languages. The main difference between a static and a dynamic language is how it handles its types (or doesn’t).

Static languages (statically typed languages) revolve around the concept of statically typed variables. In dynamic languages, variable type doesn’t need to be defined right off the bat. In other words, in the static languages variables are resolved during “compile time” and in the dynamic in the “runtime”.

What does this really mean though?

In a static language, a variable is assigned its type when the project is compiled and it stays that way during the whole execution of the application. In a dynamic language, a variable can change it’s type several times while the application is already running.

Some examples of static and dynamic languages:

Static: C , C# , F# , C++ , Java , Go

Dynamic: Javascript , Python , Ruby , PHP , Perl , Objective-C

There are some great languages in both of these categories. And how should you choose which language category is better for you or the project you are working on?

Well, let’s see what are the pros of using each language category and then make a decision.

Advantages of Static and Dynamic Languages

Static languages are generally considered to be faster because they resolve their types during the compilation phase. This helps improve the application performance and optimization. Excluding machine language and Assembly, fastest high-level languages are probably C and C++. No other languages can still match the speed of these two because of their memory management capabilities.

Besides being fast, static languages are also being fast to fail. You’ll find a lot of bugs even before the application has started because of the compiler checks. This means fewer bugs once the application is up and running.

On the other hand dynamic languages, while being slower, are much easier to write. And you can write them faster without having to think about which type to use or how to initialize it.

This comes at the price of finding more bugs in runtime due to the interpreter missing to recognize a variable’s type. At the same time, such code tends to be easier to read and is smaller overall, so the bugs generally have less space to hide in.

But, let’s not turn this into which language is better while we still can

Any language is powerful in the right hands, and knowing any language to its core and utilizing all of its features is the key to making great applications.

What is Dynamic Type in C#

So we already mentioned that C# is a statically typed language. So what does a dynamic type has to do with C#?

The dynamic type has been added to C# since version 4 as because of the need to improve interoperability with COM (Component Object Model) and other dynamic languages. While that can be achieved with reflection, dynamic 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).

C#
 dynamic intNumber = 12;
 dynamic book = new Book(isbn);

Simple enough.

How Does Dynamic Type 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:

C#
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”?

Can we do that?

Let’s see.

C#
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:

C#
Console.WriteLine(testVariable++);

The code will compile, but we are going to get an exception when we run the application:

No alt text provided for this image

RuntimeBinderException is the exception you will probably see the most when you work with dynamic type. As the exception message states, we cannot use ++ operator on the variable of the type string. So that means that 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:

C#
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.

Ok, that’s enough examples for now. Let’s dive a bit deeper into dynamic type (geeky stuff ahead).

A Short Overview of DLR

Ok, so now that we’ve seen how the dynamic type works, let’s discuss what’s behind the curtains a bit. We won’t go too deep since that would be overdoing it for this article.

DLR or Dynamic Language Runtime is what makes dynamic type easy to use in a C# which is statically typed language. DLR adds a set of services to the CLR (Common Language Runtime) that make dynamic work. DLR is open source project and can be found on this GitHub repo.

Here’s how DLR looks like:

DLR architecture

As we can see, the services DLR added to the already existing functionalities of CLR include Expression Trees, Call Site Caching and Dynamic Object Interoperability.

Let’s see what these features can do for us.

Expression Trees

Expression trees represent code in a tree-like data structure, where each node is an expression, for example, a method call or a binary operation. DLR has extended LINQ expression trees with control flow, assignment and other language-modeling nodes. If you are interested in what expression trees check out the documentation on Expression Trees in C# and How to Build Dynamic Queries With Expression Trees in C#.

Call Ste Caching 

To achieve the optimal efficiency of operation execution, DLR provides a mechanism to cache previously called operations. A dynamic call site is a place where you call a.b() or a+b on dynamic objects. DLR caches the types of objects that are used while executing these operations and stores them in a cache. If a similar operation is performed again, DLR can retrieve the information from that cache.

Dynamic Object Interoperability

To achieve operability with different languages and enable authors to implement dynamic libraries DLR provides a set of classes and interfaces that are used for this purpose: IDynamicMetaObjectProvider, DynamicMetaObjectDynamicObject, and ExpandoObject.

So having all this in mind, the advantages of DLR are:

  • Simplified portability of dynamic languages to .NET
  • Enables dynamic features in static languages
  • Improved sharing of libraries and communication between static and dynamic languages
  • Fast dispatch and invocation (using caching)

In conclusion, DLR is what makes the dynamic type work as we know it and we wouldn’t be able to use dynamic type without it and the mechanisms it provides.

Enough of the geeky stuff, let’s proceed.

Why Should We Use the Dynamic Type

By now, you probably have some ideas on where you can use dynamic type. But let’s go through some common scenarios in which dynamic could potentially improve our applications and make our lives as developers a bit easier.

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 and Intellisense for them due to the nature of dynamic type. On the other hand, if have a need to implement dynamic type everywhere, we are probably using a wrong type of language. Dynamic languages are better suited for those kinds of cases.

So what are the common cases to apply dynamic 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 a 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 an 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:

C#
EmployeeFactory employeeFactory = GetEmployeeFactory();
object firstEmployee = employeeFactory.GetFirstEmployee();
Type firstEmployeeType = firstEmployee.GetType();
object workStatusObject = firstEmployeeType.InvokeMember("GetWorkStatus", BindingFlags.InvokeMethod, null, firstEmployee, null);
WorkStatus workStatus = Enum.Parse<WorkStatus>(workStatusObject.ToString());
Console.WriteLine(workStatus);

This piece of code although very complicated and large for its purpose, retrieve 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 what the work status of the firstEmployee.

A bit complicated but it works.

Now let’s see how the same example looks like by using dynamic:

C#
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.

C#
var imNumber = 10; //implicitly typed variable
int exNumber = 10; //explicitly typed variable

Our opinion is that var improves 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 on when to use it and when not, check out this blog post: When to Use and Not Use var in C#.

Object Keyword

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:

C#
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.

C#
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 .Split() on an integer.

You get a picture.

It’s not that simple though, there are some gotchas that we should be aware of. For example, calling the .Count() on a dynamic object that has been assigned ICollection:

C#
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:

C#
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.

No alt text provided for this image

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. Sound like a miracle medicine

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 of the future articles.

So to sum it up, in this post we’ve learned:

  • What static and dynamic languages are and what the pros and cons of each are
  • About dynamic type in C# and how to implement it
  • Some situations where dynamic can come in handy
  • The difference between reflection and dynamic
  • A bit about DLR
  • Cleared out the differences between var, object and dynamic keywords
  • About RuntimeBinderException

All Rights By Author: Vladimir Pecanac



Seyed Mohammad Hosein Hoseini

I am with you on .Net Core & SQL Server & Angular

1 年

very good ??

回复
Majid Ghafari

.Net backend developer

1 年

Useful ,Tnx

回复

要查看或添加评论,请登录

Hossein S Bagheri的更多文章

  • How to Find your Dream Job

    How to Find your Dream Job

    I remember sitting in one of my career classes in college(Yazd University) listening to my professor (Dr.Mousa Zade)…

  • ????? ????? ????? ?????

    ????? ????? ????? ?????

    ????? ??????? ????? ?? ??? ?? ???????? ?? ???? ???? ??? ?????: ????? ?? ??? ?????? ??? ???? ?? (Onion, CQRS, SOA) ??…

  • AAA and SSO

    AAA and SSO

    one minute to read After 9 years of programming in variants application developing, I think one of the most concept in…

社区洞察

其他会员也浏览了