In C#,  the dynamic keyword, the ExpandoObject class, and the DynamicObject class, are the main approaches to working with dynamic objects. However, ExpandoObject and DynamicObject are often used interchangeably, so it can be quite difficult to distinguish them. In this article, we will delve into the differences between ExpandoObject, DynamicObject, and dynamic. We will better understand these concepts and how we can use them effectively in our code.

To download the source code for this article, you can visit our GitHub repository.

Let’s start.

What Is Dynamic in C#?

Dynamic is a C# static type. It was introduced in C# to provide interoperability when working with dynamically typed languages like Python, working with COM Objects, when accessing the HTML DOM, or when working with JSON. When working with objects of the dynamic type, we can try to access any member of the type even if it doesn’t exist and the compiler won’t show any error until we run the code.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

Let’s create a person object with one member:

dynamic person = new
{
    Name = "Test"
};

After that, let’s declare a new age variable and assign the value of Age from the person object to it:

var age = person.Age;

Up to this point, the code doesn’t throw any exceptions but when we run our application, we get a RuntimeBinderException. This is because the person object doesn’t have a member called Age.

Also, we can assign a value of any type to a dynamic variable:

dynamic count = 1;

Assert.IsType<int>(count);

In this method, we declare count as a dynamic variable, and assign to it the value of 1. Then, at runtime, the type of count will be an int. This means that dynamic objects will take the type of value they are assigned to.

Dynamic variables allow flexibility in our applications since variables are not checked by the compiler. However, using dynamic variables may lead to errors in our applications. If we misspell a member or try accessing a non-existent member, for instance, the application throws a RuntimeBinderException since there’s no static checking at compile time.

What Is an ExpandoObject?

The ExpandoObject class in C# allows us to instantiate objects that we can use to dynamically add or remove members at runtime. In addition to adding members, ExpandoObject also enables us to set and get the values of the object’s members.

To create an instance of ExpandoObject, we use the dynamic keyword. Since we need to dynamically add members to ExpandoObject, we need to be able to call members that don’t exist in it without having a compilation error, and that is where the dynamic keyword is useful.

Let’s create an instance of ExpandoObject:

dynamic book = new ExpandoObject();

book.Author = "John Doe";
book.Year = 2023;

Assert.Equal("John Doe", book.Author);
Assert.Equal(2023, book.Year);

Using the . operator, we add two new properties with values to the book object. Then, we just use the . operator again to access those values. 

Let’s also see how we can add methods to an ExpandoObject:

book.copiesSold = 1000;
book.Sell = (Action)(() => { book.copiesSold++; });
book.Sell();

We first add copiesSold member with a value of 1000. Then, we add the Sell() method as a delegate which increments the value of copiesSold.

Calling this method, the value of copiesSold increases by one every time.

Enumerating ExpandoObject Members

The ExpandoObject class implements the IDictionary<string, object> interface. This means that every time we add new members to an object, they are stored as key-value pairs.

To get all the values of the ExpandoObject instance, we loop over the object, enumerating the values:

dynamic country = new ExpandoObject();

country.Continent = "Asia";
country.Population = "3 Billion people";

foreach (KeyValuePair<string, object> keyValuePair in country)
{
    _testOutputHelper.WriteLine($"{keyValuePair.Key} : {keyValuePair.Value}");
}

We add a foreach loop, in which we create an instance of the KeyValuePair structure that we use to get the values of the dynamic object. We then print the results to the console. This comes in handy in situations where we don’t know the format of incoming data beforehand.

When we run this method, we get:

Continent : Asia
Population : 3 Billion people

Now that we’ve learned how to add and read values from an ExpandoObject, let’s see how we can remove properties from the object.

Removing Properties From an ExpandoObject

Let’s create a new dynamic object and remove a property from it:

dynamic person = new ExpandoObject();

person.Age = 30;
person.Name = "Jane Doe";

((IDictionary<string, object>)person).Remove("Age");

We first create a person object and add two properties to it. After that, we cast the person object to the  IDictionary<string, object> interface. Then, we call the Remove() method passing the Age property. This deletes the property from the person object.

ExpandoObject Property Changes

The ExpandoObject class also implements the  INotifyPropertyChanged interface, which fires a PropertyChanged event every time we add, remove or update the object properties. The event notifies subscribers of the changes in the properties of the object.

Let’s examine this:

dynamic person = new ExpandoObject();

((INotifyPropertyChanged)person).PropertyChanged += (_, e) =>
{
    _testOutputHelper.WriteLine($"Property changed: {e.PropertyName}");
};

person.Name = "John Doe";

We first declare a new person dynamic object. Then, we subscribe to changes in the object by raising a PropertyChanged event. After that, we add a new Name property to the person object.

Calling this method, we get:

Property changed: Name

What Is a DynamicObject?

The DynamicObject class lets us create custom dynamic objects. It helps us specify the operations we can perform on dynamic objects and how we perform the operations.

However, we can only inherit the class in our applications since we cannot instantiate it directly. After inheriting the class, we override the methods we need to implement custom logic.

DynamicObject Methods

The main methods we’ll look at in this article are:

The TryGetMember() method allows us to customize the behavior of accessing member values dynamically at runtime. To customize the behavior, we override the method:

public override bool TryGetMember(GetMemberBinder binder, out object result)

The GetMemberBinder parameter provides information about the object whose member is being accessed. The result parameter is the result of the get operation.

The TrySetMember() method allows us to customize operations for setting member values of dynamic objects:

public override bool TrySetMember(SetMemberBinder binder, object value)

Similar to TryGetMethod this method also takes two parameters. The SetMemberBinder parameter provides information about the member being set. The value parameter is the value assigned to the accessed member. 

Both methods should return true if the operation is successful, otherwise, they should return false.

Working With the DynamicObject Type

Let’s create a new Person class that inherits DynamicObject:

public class Person : DynamicObject
{
    private readonly Dictionary<string, object?> _personalInformation;

    public Person()
    {
        _personalInformation = new Dictionary<string, object?>();
    }
}

The class has one field of type Dictionary<string, object?> and a constructor. 

Let’s now implement two methods of the DynamicObject class:

public class Person : DynamicObject
{
    private readonly Dictionary<string, object?> _personalInformation;

    public Person()
    {
        _personalInformation = new Dictionary<string, object?>();
    }

    public override bool TryGetMember(GetMemberBinder binder, out object? result)
    {
        var key = binder.Name;

        return _personalInformation.TryGetValue(key, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object? value)
    {
        var key = binder.Name;
        _personalInformation[key] = value;

        return true;
    }
}

First, we override the TryGetMember() method to get the value of a property from the _personalIformation dictionary.  Then, the TrySetMember() method helps us set the value of a property on the _personalInformation dictionary.

To test these methods, we can instantiate the Person class as a dynamic object:

dynamic dynamicPerson = new Person();

person.Name = "Test";
person.Age = 30;
person.Address = "1234 Good Street";

In this case, we’re adding three new properties to the person dynamic object. 

Let’s add a new method to the Person class:

public void PrintInfo()
{
    Console.WriteLine("Personal Information:");

    foreach (var info in _personalInformation)
    {
        Console.WriteLine($"{info.Key}: {info.Value}");
    }
}

Calling this method, we get:

Personal Information:
Name: Test
Age: 30
Address: 1234 Good Street

The PrintInfo() method dynamically reads values from the person object and prints them to the console.

This is a very simplified use case of the DynamicObject class. The method can however be inherited and customized further. The perfect use case would be reading values from a configuration file.

Having looked at the dynamic types individually, let’s proceed to look at the differences between them.

Differences Between ExpandoObject, DynamicObject, and Dynamic

The ExpandoObject class allows us to add members to a dynamic object instance at runtime and use them dynamically. Internally, it implements the  IDictionary<string, object> interface, which enables it to store properties and values in key-value pairs.

To add or access properties from the ExpandoObject we can either use the . operator or treat it as a dictionary. Also, we don’t need to explicitly define another class or override members in order to use it in our applications.

The DynamicObject is a more advanced class that allows us to customize the behavior of dynamic objects. Compared to the ExpandoObject class, the DynamicObject class is more flexible and more powerful because we can use it to create custom logic for operations on dynamic objects. It is a good choice for scenarios where we need to have more control over the dynamic objects we create

dynamic, on the other hand, is a type that helps us create dynamically-typed objects. When we declare a variable as dynamic, the compiler does not check the type of the variable. With this in mind, we can assign a value of any type to the dynamic object. We can also perform operations on the objects without compile-time checks. It is also the type that should be used in order to work with ExpandoObject and DynamicObject.

Conclusion

In this article, we have learned about the differences between ExpandoObject, DynamicObject, and dynamic. We have looked at their features and how we can make use of them in our applications. Knowing the appropriate use case for each of them can be quite difficult, especially for beginners. However, we hope this article has provided more insight.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!