Create objects dynamically in C#
Today I ran into a new class that I had yet to see in the .NET Framework. The ExpandoObject
within the System.Dynamic
namespace. The class is used as a dynamic
object, which I have used in the past.
With a dynamic
object, you can do:
static void Main(string[] args)
{
dynamic car = new Object();
car.Make = "G35";
car.Year = 2014;
}
And it would compile, even though the car variable is of a Object
type, therefore the Make and Year properties don't exist. This is nice in some cases, but not something that I have really had a large use for as of yet.
Today I learned that you can essentially construct a Type at runtime using the dynamic type. In the example above, the properties that I assigned to the dynamic car object were hard-coded at compile time and not runtime, so they can be used without issue. The .NET Framework ships with an object callec ExpandoObject
that allows you to attach properties and methods to a dynamic object during runtime, without recompiling the source.
static void Main(string[] args)
{
dynamic car = new ExpandoObject();
car.Make = "G35";
car.Year = 2014;
}
So what gives? The code above looks the exact same right? With the exception that the dynamic type changed from a object
to a ExpandoObject
. The ExpandoObject
is typed to a dynamic, allowing you to continue to assign properties to the object at compile time. In the event that you want to assign a property to the object at runtime, you invoke the ExpandoObject
's IDictionary.Add method.
static void Main(string[] args)
{
dynamic car = new ExpandoObject();
car.Make = "G35";
car.Year = 2014;
((IDictionary<string, object>)car).Add("TopSpeed", 180);
}
That essentially adds a new property called "TopSpeed"
to the car object and assigns it a value of 180. This can be extremely useful in the event that you are pulling data from an external source such as a local file or a database, and want to construct an object based off of the information loaded. You could in essence do something like this:
dynamic databaseStuff = new ExpandoObject();
foreach (DataColumn column in table.Columns)
{
((IDictionary<string, object>)databaseStuff).Add(column.ColumnName, null);
}
Now I have a object who's properties match that of my database Table schema. You can of course assign the value of the column row to the property you just created rather than assigning it to null like I did in my example.
Now, the ((IDictionary<string, object>)someVariable).Add();
method is a real eye sore. You could make that easier by wrapping it.
public class DynamicObject
{
public dynamic Instance = new ExpandoObject();
public void AddProperty(string name, object value)
{
((IDictionary<string, object>)this.Instance).Add(name, value);
}
public dynamic GetProperty(string name)
{
if (((IDictionary<string, object>)this.Instance).ContainsKey(name))
return ((IDictionary<string, object>)this.Instance)[name];
else
return null;
}
}
With the above code, we can easily add properties to an object at runtime just by invoking the AddProperty
method, and retreive the value of the properties either by accessing the dynamic object itself (Instance
) or by invoking the GetProperty
method. The following code will output the property values to the console. Note that while I am creating the TopSpeed
property using our AddProperty
method, I am not forced to access it via the GetProperty
method, but I can also access it directly from the Instance
property since it is a dynamic Type.
static void Main(string[] args)
{
var car = new DynamicObject();
car.Instance.Make = "G35";
car.Instance.Year = 2014;
car.AddProperty("TopSpeed", 180);
int topSpeed = car.GetProperty("TopSpeed");
string msg = string.Format("The Top Speed of a {0} is {1}", car.Instance.Make, car.Instance.TopSpeed);
Console.WriteLine(msg);
Console.ReadKey();
}
There are a whole lot of other cool things you could do with this, such as constructing a method at runtime. The above example has been modified to build a method for use at runtime with the car object and a road object. Methods could be obtained by loading code at runtime and assigning methods to a dynamic object as needed. Really useful for application plugins or anything that might need this approach.
We'll start with our wrapper object, by adding a AddMethod
method.
public void AddMethod(Action methodBody, string methodName)
{
this.AddProperty(methodName, methodBody);
}
Since a Action is nothing more than a method delegate, we can attach it as a property and invoke it later. This lets us pass a method into our object and store it as a property. Next, we create a method, add it and invoke it.
static void Main(string[] args)
{
var car = new DynamicObject();
// Create properties
car.Instance.Make = "G35";
car.Instance.Year = 2014;
car.Instance.CurrentSpeed = 45;
// Add property in a more dynamic manor.
car.AddProperty("TopSpeed", 180);
// Get the property using our wrapper for a more dynamic manor.
int topSpeed = car.GetProperty("TopSpeed");
// Access a property we added via our method, directly.
car.Instance.TopSpeed = 200;
// Create a Road object.
var road = new DynamicObject();
road.AddProperty("SpeedLimit", 65);
// Add a custom method.
car.AddMethod(() => { car.Instance.CurrentSpeed = road.Instance.SpeedLimit; }, "MatchSpeedLimit");
// Invoke our method. Sets the CurrentSpeed (45) to SpeedLimit (65)
car.Instance.MatchSpeedLimit();
Console.WriteLine("Current car speed is {0}", car.Instance.CurrentSpeed);
Console.ReadKey();
}
That's all there is to it! Of course, you could do all of this directly without the wrapper by using the IDictionary object everywhere, but that's a bit of a pain. I prefer this approach for myself.
The following is the entire source file in full for use.
using System;
using System.Collections.Generic;
using System.Dynamic;
namespace DynamicObjects
{
public class DynamicObject
{
public dynamic Instance = new ExpandoObject();
public void AddProperty(string name, object value)
{
((IDictionary<string, object>)this.Instance).Add(name, value);
}
public dynamic GetProperty(string name)
{
if (((IDictionary<string, object>)this.Instance).ContainsKey(name))
return ((IDictionary<string, object>)this.Instance)[name];
else
return null;
}
public void AddMethod(Action methodBody, string methodName)
{
this.AddProperty(methodName, methodBody);
}
}
class Program
{
static void Main(string[] args)
{
var car = new DynamicObject();
// Create properties
car.Instance.Make = "G35";
car.Instance.Year = 2014;
car.Instance.CurrentSpeed = 45;
// Add property in a more dynamic manor.
car.AddProperty("TopSpeed", 180);
// Get the property using our wrapper for a more dynamic manor.
int topSpeed = car.GetProperty("TopSpeed");
// Access a property we added via our method, directly.
car.Instance.TopSpeed = 200;
// Create a Road object.
var road = new DynamicObject();
road.AddProperty("SpeedLimit", 65);
// Add a custom method.
car.AddMethod(() => { car.Instance.CurrentSpeed = road.Instance.SpeedLimit; }, "MatchSpeedLimit");
// Invoke our method. Sets the CurrentSpeed (45) to SpeedLimit (65)
car.Instance.MatchSpeedLimit();
Console.WriteLine("Current car speed is {0}", car.Instance.CurrentSpeed);
Console.ReadKey();
}
}
}