C# Dynamic Type
Introduction
The dynamic
type is a powerful feature introduced in C# 4.0 that enables flexible programming by bypassing compile-time type checking. Unlike statically typed variables in C#, operations on dynamic objects are resolved at runtime rather than compile time. This makes dynamic
particularly useful when dealing with data from dynamic sources or interoperating with dynamic languages and COM objects.
In this tutorial, you'll learn:
- What the dynamic type is and how it differs from other types
- How to use dynamic variables in C#
- When to use (and when to avoid) the dynamic type
- Real-world applications of dynamic typing
Understanding the Dynamic Type
What is the Dynamic Type?
The dynamic
type is a static type that tells the compiler to skip type checking during compilation. When you declare a variable as dynamic
, the compiler treats it specially:
dynamic myVariable = 10; // myVariable is an integer at runtime
With dynamic
variables, operations that normally would be checked at compile time are deferred until runtime:
dynamic myVariable = 10;
myVariable = myVariable + 5; // Valid: adds 5 to 10
myVariable = myVariable.ToUpper(); // Runtime error: integers don't have ToUpper()
Dynamic vs. var vs. object
Let's compare dynamic
with var
and object
to understand the key differences:
// var - implicitly typed but statically resolved at compile time
var number = 10;
// number.NonExistentMethod(); // Compile-time error
// object - explicit boxing, requires casting for specific operations
object obj = 10;
// int result = obj + 5; // Compile-time error
int result = (int)obj + 5; // Valid with explicit casting
// dynamic - resolved at runtime
dynamic dyn = 10;
dyn = dyn + 5; // Valid, no casting needed
dyn = "Hello"; // Can change type at runtime
dyn = dyn.ToUpper(); // Valid, resolved at runtime
Basic Usage of Dynamic Type
Creating and Using Dynamic Variables
Here's a simple example of creating and using dynamic variables:
using System;
class Program
{
static void Main()
{
// Create a dynamic variable
dynamic dynamicValue = 100;
// Perform operations
Console.WriteLine($"Original value: {dynamicValue}");
// We can perform integer operations
dynamicValue += 50;
Console.WriteLine($"After addition: {dynamicValue}");
// We can change the type
dynamicValue = "Hello, Dynamic World!";
Console.WriteLine($"New string value: {dynamicValue}");
// We can call string methods
Console.WriteLine($"Uppercase: {dynamicValue.ToUpper()}");
// We can even assign it to an object of a custom class
dynamicValue = new Person { Name = "John", Age = 30 };
Console.WriteLine($"Person's name: {dynamicValue.Name}");
}
}
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
Output:
Original value: 100
After addition: 150
New string value: Hello, Dynamic World!
Uppercase: HELLO, DYNAMIC WORLD!
Person's name: John
Handling Dynamic Type Errors
Since type checking is done at runtime with dynamic types, errors that would normally be caught at compile time aren't discovered until the program runs:
using System;
class Program
{
static void Main()
{
dynamic dyn = 100;
try
{
// This will cause a runtime error
Console.WriteLine(dyn.Substring(1, 3));
}
catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex)
{
Console.WriteLine($"Runtime error: {ex.Message}");
}
}
}
Output:
Runtime error: 'int' does not contain a definition for 'Substring'
Advanced Dynamic Type Usage
Working with Dynamic Objects
C# provides the ExpandoObject
class that lets you create objects with properties and methods added dynamically at runtime:
using System;
using System.Dynamic;
class Program
{
static void Main()
{
// Create a dynamic ExpandoObject
dynamic person = new ExpandoObject();
// Add properties dynamically
person.Name = "Alice";
person.Age = 28;
// Add a method dynamically
person.Greet = (Action)(() =>
Console.WriteLine($"Hello, my name is {person.Name} and I'm {person.Age} years old!")
);
// Use the dynamic object
Console.WriteLine($"Name: {person.Name}");
Console.WriteLine($"Age: {person.Age}");
person.Greet();
// Add new property at runtime
person.Occupation = "Software Developer";
Console.WriteLine($"Occupation: {person.Occupation}");
}
}
Output:
Name: Alice
Age: 28
Hello, my name is Alice and I'm 28 years old!
Occupation: Software Developer
Creating Custom Dynamic Objects
You can create your own dynamic objects by implementing the DynamicObject
class:
using System;
using System.Dynamic;
using System.Collections.Generic;
class Program
{
static void Main()
{
dynamic dynamicDictionary = new DynamicDictionary();
// Set properties dynamically
dynamicDictionary.FirstName = "John";
dynamicDictionary.LastName = "Smith";
dynamicDictionary.Age = 42;
// Get properties
Console.WriteLine($"Full name: {dynamicDictionary.FirstName} {dynamicDictionary.LastName}");
Console.WriteLine($"Age: {dynamicDictionary.Age}");
// Try to access non-existent property
Console.WriteLine($"Email exists: {dynamicDictionary.HasProperty("Email")}");
}
}
// A dynamic dictionary that stores properties
public class DynamicDictionary : DynamicObject
{
// The inner dictionary
private Dictionary<string, object> _dictionary = new Dictionary<string, object>();
// Override TryGetMember to handle property access
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
string name = binder.Name;
return _dictionary.TryGetValue(name, out result);
}
// Override TrySetMember to handle property assignment
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_dictionary[binder.Name] = value;
return true;
}
// Custom method to check if a property exists
public bool HasProperty(string name)
{
return _dictionary.ContainsKey(name);
}
}
Output:
Full name: John Smith
Age: 42
Email exists: False
Practical Applications
JSON Deserialization
The dynamic
type is particularly useful when working with JSON data where the structure might not be known at compile time:
using System;
using System.Text.Json;
class Program
{
static void Main()
{
// JSON string with arbitrary structure
string jsonString = @"{
""name"": ""Product X"",
""price"": 29.99,
""specifications"": {
""weight"": ""1.5 kg"",
""dimensions"": ""10 x 20 x 5 cm""
},
""inStock"": true
}";
// Parse JSON to dynamic object
dynamic jsonData = JsonSerializer.Deserialize<System.Text.Json.JsonDocument>(jsonString)
.RootElement.JsonElementAsDynamic();
// Access properties dynamically
Console.WriteLine($"Product: {jsonData.name}");
Console.WriteLine($"Price: ${jsonData.price}");
Console.WriteLine($"Weight: {jsonData.specifications.weight}");
Console.WriteLine($"In Stock: {(jsonData.inStock ? "Yes" : "No")}");
}
}
// Extension to make JsonElement work better with dynamic
public static class JsonExtensions
{
public class DynamicJsonElement : DynamicObject
{
private readonly JsonElement _element;
public DynamicJsonElement(JsonElement element)
{
_element = element;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (_element.ValueKind == JsonValueKind.Object &&
_element.TryGetProperty(binder.Name, out JsonElement property))
{
result = property.JsonElementAsDynamic();
return true;
}
result = null;
return false;
}
}
public static dynamic JsonElementAsDynamic(this JsonElement element)
{
switch (element.ValueKind)
{
case JsonValueKind.String:
return element.GetString();
case JsonValueKind.Number:
if (element.TryGetInt32(out int intValue))
return intValue;
return element.GetDouble();
case JsonValueKind.True:
return true;
case JsonValueKind.False:
return false;
case JsonValueKind.Object:
return new DynamicJsonElement(element);
default:
return null;
}
}
}
Output:
Product: Product X
Price: $29.99
Weight: 1.5 kg
In Stock: Yes
COM Interoperability
One of the most practical uses of dynamic
is for COM interoperability, such as when working with Microsoft Office applications:
using System;
class Program
{
static void ExcelExample()
{
// Create Excel application instance
Type excelType = Type.GetTypeFromProgID("Excel.Application");
dynamic excel = Activator.CreateInstance(excelType);
try
{
// Make Excel visible
excel.Visible = true;
// Add a new workbook
dynamic workbook = excel.Workbooks.Add();
dynamic worksheet = workbook.Worksheets[1];
// Add some data
worksheet.Cells[1, 1] = "Name";
worksheet.Cells[1, 2] = "Score";
worksheet.Cells[2, 1] = "Alice";
worksheet.Cells[2, 2] = 95;
worksheet.Cells[3, 1] = "Bob";
worksheet.Cells[3, 2] = 87;
// Format headers
dynamic headerRange = worksheet.Range["A1:B1"];
headerRange.Font.Bold = true;
Console.WriteLine("Excel document created. Press Enter to close.");
Console.ReadLine();
}
finally
{
// Clean up
excel.Quit();
// Release COM object
System.Runtime.InteropServices.Marshal.ReleaseComObject(excel);
}
}
static void Main()
{
// Note: This would require Microsoft Excel to be installed
Console.WriteLine("This example requires Microsoft Excel. Run it? (y/n)");
if (Console.ReadLine().ToLower() == "y")
{
try
{
ExcelExample();
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
}
Performance Considerations
While dynamic
is powerful, it comes with performance overhead since type checking happens at runtime. Here's a comparison:
using System;
using System.Diagnostics;
class Program
{
static void Main()
{
const int iterations = 10000000;
// Test with regular int
Stopwatch sw1 = Stopwatch.StartNew();
int regularInt = 5;
for (int i = 0; i < iterations; i++)
{
regularInt += 1;
}
sw1.Stop();
// Test with dynamic
Stopwatch sw2 = Stopwatch.StartNew();
dynamic dynamicInt = 5;
for (int i = 0; i < iterations; i++)
{
dynamicInt += 1;
}
sw2.Stop();
Console.WriteLine($"Static type (int): {sw1.ElapsedMilliseconds} ms");
Console.WriteLine($"Dynamic type: {sw2.ElapsedMilliseconds} ms");
Console.WriteLine($"Dynamic is {(double)sw2.ElapsedMilliseconds / sw1.ElapsedMilliseconds:F1}x slower");
}
}
Output (results may vary):
Static type (int): 6 ms
Dynamic type: 402 ms
Dynamic is 67.0x slower
When to Use Dynamic Type
Good Use Cases
- Working with COM interop (Office automation, legacy COM components)
- Parsing JSON or XML with unknown structure at compile time
- Interacting with dynamic languages like Python or JavaScript
- Creating domain-specific languages within C#
- Simplifying reflection-heavy code
When to Avoid
- Performance-critical code
- Code where compile-time type safety is important
- Public APIs (prefer strongly-typed interfaces)
- Large applications where maintainability is a concern
- When other features like generics would be more appropriate
Summary
The dynamic
type in C# provides flexibility by delaying type checking until runtime, allowing for more dynamic programming patterns in an otherwise statically typed language. We've explored:
- How the
dynamic
type works and how it differs fromvar
andobject
- Basic and advanced usage of dynamic variables
- Creating custom dynamic objects
- Practical applications in JSON handling and COM interoperability
- Performance implications and best practices
While powerful, the dynamic type should be used judiciously, with an understanding of its performance implications and the trade-offs involved in bypassing C#'s compile-time type checking.
Additional Resources
Exercises
- Create a dynamic calculator that can perform operations on different types (integers, decimals, strings)
- Implement a custom DynamicObject that validates property values before assignment
- Create a simple data access layer using dynamic to handle database results
- Use dynamic to parse and manipulate different JSON structures
- Compare the performance of reflection-based property access versus dynamic-based access
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)