Using ExpandoObject as a Dynamic Property Bag in C

The ExpandoObject class in C# provides an easy way to add, modify, and delete object properties dynamically at runtime. This article explains what ExpandoObject is, demonstrates how to use it with the dynamic keyword, and shows how casting it to an IDictionary<string, object?> enables you to treat it as a flexible, dynamic property bag.

What Is ExpandoObject?

Defined in the System.Dynamic namespace, ExpandoObject represents an object whose members can be dynamically added and removed at runtime. It implements both dynamic dispatching via the Dynamic Language Runtime (DLR) and standard collection interfaces such as IDictionary<string, object?> and INotifyPropertyChanged.

Because it implements IDictionary<string, object?>, an instance of ExpandoObject acts as a dynamic property bag: you can interact with it using conventional object member syntax, or you can manipulate its internal state as key-value pairs.

Basic Usage with the dynamic Keyword

To use ExpandoObject with member-access syntax, declare your variable with the dynamic keyword. You can immediately assign values to properties that were never explicitly defined on a class.

using System;
using System.Dynamic;

dynamic person = new ExpandoObject();

// Adding properties on the fly
person.FirstName = "Jane";
person.LastName = "Doe";
person.Age = 30;

// Adding dynamic behavior (methods/delegates)
person.GetFullName = (Func<string>)(() => $"{person.FirstName} {person.LastName}");

Console.WriteLine(person.GetFullName()); // Outputs: Jane Doe
Console.WriteLine(person.Age);          // Outputs: 30

Treating ExpandoObject as a Property Bag

The true power of ExpandoObject as a property bag appears when you cast it to IDictionary<string, object?>. This allows you to inspect properties, read keys programmatically, remove properties, or add data when property names are only known at runtime.

Adding and Accessing Properties Dynamically

You can set values using string keys and subsequently access them via dynamic syntax, or vice versa.

using System;
using System.Collections.Generic;
using System.Dynamic;

dynamic bag = new ExpandoObject();
var propertyBag = (IDictionary<string, object?>)bag;

// Add values using dictionary syntax
propertyBag["Department"] = "Engineering";
propertyBag["Salary"] = 95000;

// Access using dynamic member syntax
Console.WriteLine(bag.Department); // Outputs: Engineering

// Access using dictionary syntax
Console.WriteLine(propertyBag["Salary"]); // Outputs: 95000

Enumerating Properties

Because it implements IEnumerable<KeyValuePair<string, object?>>, you can iterate over all currently defined members:

foreach (var kvp in (IDictionary<string, object?>)bag)
{
    Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}

Checking for Property Existence and Deleting Properties

Unlike standard static types, an ExpandoObject allows you to remove properties or verify their presence safely without throwing a RuntimeBinderException.

var propertyBag = (IDictionary<string, object?>)bag;

// Check if a property exists
if (propertyBag.ContainsKey("Department"))
{
    // Remove the property
    propertyBag.Remove("Department");
}

// Attempting to access bag.Department now throws a RuntimeBinderException

Common Use Cases

  • Dynamic Data Mapping: Consuming arbitrary JSON or CSV structures where schemas vary across records.
  • View Models and View Bags: Passing loosely-structured data across application layers without creating single-use DTO classes.
  • Database Query Results: Mapping dynamic columns returned from flexible or schema-less SQL queries.