Background
To truly understand how to process the dynamic lookup calls, you must have a relatively advanced understanding of Expression Trees and how to visit them properly. The subject is beyond the scope of this article, but is crucial in understanding the nuances of processing such calls. Please refer to the MSDN text for further information about this.
Why Dynamic Programming
Examine the following listing:
function NewPerson(name, age) {
var person = new Object();
person.FullName = name;
person.Age = age;
person.Talk = function()
{
alert("My name is " + name + " i am " + age + " years old");
};
return person
}
var oke = NewPerson("oke", 4);
oke.RideBike = function()
{
alert("look daddy!");
};
var azuka = NewPerson("azuka", 2);
azuka.WearDiper = function()
{
alert("its good to be 2 ;-)");
};
Can this type of functionality be implemented in C# using the built-in features alone? Before you answer consider the following. The listing below depicts the classification of a Person
and the creation of two instances of that class, oke
and azuka
. As can be seen above, each Person
class has two public properties, Age
and Name
, which are set at construction time. Once created, the oke
instance adds an additional method RideBike
, while the azuka
instance adds its own additional method WearDiper
. To go back to the question, the answer is yes. Here is a C# equivalent:
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
public void Talk()
{
Console.WriteLine("My name is " + Name + " i am " +
Age + " years old");
}
}
public class OkePerson : Person
{
public void RideBike()
{
Console.WriteLine("Look Daddy!");
}
}
public class AzukaPerson
{
public void WearDiper()
{
Console.WriteLine("its good to be two ;-)");
}
}
OkePerson oke = new OkePerson();
AzukaPerson azuka = new AzukaPerson();
Of course, with each additional instance, with a unique behavior comes a new subtype that supports the behavior; each of which might not be ubiquitous enough to require its own type. There are several variations to this technique that all introduce the same unnecessary bloat. This is in fact one of the main problems with C#'s implementation of static types. As the name implies, static types are known at compile time, which in the case of C# means, must already be defined at compile time. This necessitates the construction of every possible type and behavior before compilation. When the objects are large with lots of state, behavior, and such, this is not such a bad thing, but when I need some silly one-off style object as a simple data container, or a slight mutation of a pre-existing object as with the example above, this can become relatively burdensome. In fact, I believe that such (relatively meaningless) types introduce an unnecessary complexity.
Enter LINQ, Lambdas, and Anonymous Types
I think the C# team realized this and made steps to help alleviate it with LINQ. By allowing the coder to create dynamic projections that could be encapsulated into the var
type (through type inference), we are able to create all sorts of anonymous types without having to go somewhere and define them first in order to get everything to compile. Here is how it might look:
Action talk_method = () =>
{
Console.WriteLine("Hi daddy!");
};
Action ride_bike = () =>
{
Console.WriteLine("Look Daddy!");
};
Action wear_diper = () =>
{
Console.WriteLine("It's good to be two ;-)");
};
var oke = new
{
Name = "Oke",
Age = 4,
Talk = talk_method,
RideBike = ride_bike,
};
var azuka = new
{
Name = "Azuka",
Age = 2,
Talk = talk_method,
WearDiper = wear_diper
};
oke.RideBike();
azuka.WearDiper();
To be clear, this is not really mutation; the anonymous types associated with the oke
and azuka
variables are completely different. So what is wrong with that? Nothing. But if you want behavior similar to the above sample, then LINQ falls short. Firstly, a problem with projections - and local type inference - as they relate to C# is that it is localized to a method. This is a marked disadvantage when attempting to expose your dynamic types beyond the scope of the method in which it was created. Since var
cannot be used as a return type and the actual type of the projection is unknown, your projections must be converted to known types in order to use them elsewhere (more specifically, outside of the type providing method). This kind of makes this approach not very useful. There are ways around this however; for instance, we can return var
as object
in a Create method and use Reflection to get and set the values of Name
and Age
as well as to call the Talk
function. Here is a sample of how that can be done.
public static object CreatePerson(string name, int age)
{
Action talk_method = () =>
{
Console.WriteLine("Hi daddy!");
};
var oke = new
{
Name = name,
Age = age,
Talk = talk_method,
};
return oke;
}
If you copy paste and run this sample, you will notice that you get a runtime error on the third line in the main segment of the code. This is when we try to set the value of the Name
property of the Person
object we created in line 1. This leads us to the second problem with the LINQ approach: LINQ based dynamic types are immutable, so the types I created are really nothing but data containers. This means that I can't change the value of Name
or Age
after the fact. Bummer! What's even worse than that is the fact that C# interface conversions are based on static type declaration as opposed to signature compliance. So although my anonymous type in variable t
has a public Name
property of type string
, a public Age
property of type int
, and a public Talk
property of type Action
, I can't convert from one type to the other. I will continue to argue that there is no need for interface declarations in type definitions, but I suspect I'm the only audience.
The other main feature missing here is the instance level mutation that allows me to modify the classification of a type at runtime. In the JavaScript sample, the two persons object are created first, then additional individual behavior is applied to each one. The oke
instance is given a RideBike
method, and the azuka
instance is given the WearDiper
function. There is no built in functionality in C# to reproduce this feature, but there are patterns that can be applied to resolve this. Historically, this has been done in C# by using V-Tables to represent the dynamic context of the given instance. The listing below illustrates a possible representation of a simple v-table for holding arguments which take no arguments and return no values.
static Dictionary<object, Dictionary<string, Action>> global_vtable;
When coupled with the Extension Methods functionality of C# 3.0, the results can be quite spectacular. The listing below illustrates the use of this mechanism to achieve some semblance of dynamic programming:
public class DynamicFunctionContext<RETVAL>
{
object _internal;
Func<RETVAL> _function;
string _function_name;
public DynamicFunctionContext(object obj, string function_name)
{
_internal = obj;
this._function_name = function_name;
}
public bool IsDefined
{
get
{
if (_function != null)
return true;
else
{
object prototype = _internal.GetPrototype();
if (prototype != null)
return prototype.function<RETVAL>(_function_name).IsDefined;
else
return false;
}
}
}
public Func<RETVAL> define
{
set
{
_function = value;
}
}
public Func<RETVAL> call
{
get
{
if (_function != null)
return _function;
else
{
object prototype = _internal.GetPrototype();
if (prototype != null)
return prototype.function<RETVAL>(_function_name).call ;
}
throw new MissingMethodException("The specified function has " +
"not been defined for this instance");
}
}
}
public static class ObjectExtentions
{
internal static Dictionary<object, object> _prototype_list =
new Dictionary<object, object>();
static Dictionary<object, FunctionDictionary> _context_list =
new Dictionary<object, FunctionDictionary>();
#region Prototype Management
public static void SetPrototype(this object instance, object prototype)
{
if (_prototype_list.ContainsKey(instance))
{
_prototype_list[instance] = prototype;
}
else
{
_prototype_list.Add(instance, prototype);
}
}
public static object GetPrototype(this object instance)
{
object retval = null;
if (_prototype_list.ContainsKey(instance))
{
retval = _prototype_list[instance];
}
return retval;
}
#endregion
#region Dynamic Function Context
public static DynamicFunctionContext<RETVAL> function<RETVAL>(this object instance,
string function_name)
{
DynamicFunctionContext<RETVAL> ctx = null;
if (_context_list.ContainsKey(instance))
{
Dictionary<string, object> temp = _context_list[instance];
if (temp.ContainsKey(function_name))
{
object context = temp[function_name];
if (context is DynamicFunctionContext<RETVAL>)
ctx = context as DynamicFunctionContext<RETVAL>;
else
throw new Exception("no function with this return type is defined");
}
else
{
ctx = new DynamicFunctionContext<RETVAL>(instance, function_name);
temp.Add(function_name, ctx);
}
}
else
{
ctx = new DynamicFunctionContext<RETVAL>(instance, function_name);
FunctionDictionary temp = new FunctionDictionary();
temp.Add(function_name, ctx);
_context_list.Add(instance, temp);
}
return ctx;
}
#endregion
#region Internal Types
public class FunctionDictionary : Dictionary<string, object> { }
#endregion
}
The listing above is an excerpt from the downloadable code. It expands on the idea of using a Dictionary
to store a list of functions keyed by the object instance. This functionality should mimic the ability to mutate object instances from each other at runtime. The result of this is that CreatePerson
can now be defined like this:
public static object CreatePerson2(string name, int age)
{
var person = new Person
{
Name = name,
Age = age,
};
return person;
}
And each instance of the created type can be mutated as follows:
var oke = CreatePerson2("oke", 4);
oke.function<bool>("RideBike").define = () =>
{
Console.WriteLine("Look Daddy!");
return true;
};
var azuka = CreatePerson2("azuka", 2);
azuka.function<bool>("WearDiper").define = () =>
{
Console.WriteLine("Its good to be 2!");
return true;
};
Enter C# 4.0
C# 4.0 introduces the dynamic
keyword and the ability to defer resolution of a given type's member till runtime. Wouldn't it be great if we could mimic the syntax from JavaScript exactly and do something like the listing below?
dynamic person = new object();
person.Name = name;
person.Age = age;
In the example above, the Person
instance of type object
has been mutated with a Name
and Age
property. Now I am sure there are a lot of cool uses of the whole dynamic thing outside of the framework; for instance, when interacting with unmanaged COM types, it comes in pretty handy, but when the underlying type is a managed type, then the basic usage of dynamic
is just a handy dandy way to express standard Reflection. The great thing about this when it comes to the previously discussed Lambdas is that Reflection need no longer be overtly expressed in order to access a desired member. If we were to change our first definition of CreatePerson
to the following:
public static dynamic CreatePerson(string name, int age)
{
Action talk_method = () =>
{
Console.WriteLine("Hi daddy!");
};
var oke = new
{
Name = name,
Age = age,
Talk = talk_method,
};
return oke;
}
We could access Name
, Age
, or Talk
as follows:
dynamic oke = CreatePerson("oke", 4);
string str = oke.Name;
Internally, the following is still being done:
oke.GetType().GetProperty("Name").GetValue(oke, null);
However, since using Reflection ultimately means having a previously defined type to reflect upon, this base implementation is relatively useless in helping us define dynamic types from scratch. This means that the example above which attempted to mutate object with two additional properties will fail at runtime since the underlying type object does not have those properties defined in it. Fortunately, dynamic
also provides the IDynamicObject
interface for those of us who want to do our own dynamic dispatching. How this works is beyond the scope of this already long article, but to summarize, it allows you to intercept the calls made at runtime by the framework to resolve invocations made on a given dynamic type and literally do what you want. Let's explore this step by step so it's clear what we mean here. We already know that instead of doing something like this:
object obj = new object();
obj.ToString();
you can use the dynamic types and do this:
dynamic obj = new object();
obj.ToString();
In the first instance, ToString
is validated at compile time. If the function does not exist in the exact signature that it is being called with, your code will not compile. In the second example, ToString
is ignored at compile time. Whether or not the function exists, the code will compile. At runtime, when the symbol ToString
associated with the instance obj
of type object
is encountered, the runtime will attempt to evaluate it (in the case of object using reflection). When the type of the dynamic is a type which implements IDynamicObject
, it will use that type's implementation of IDynamicObject
to evaluate the symbol. This is a very powerful feature. In the context of our discussion, I mean that we can do what JavaScript already does and add members to types at runtime!
The implementation of this requires a number of things. First, we must define a type to implement IDynamicObject
. The listing below defines a new type DynamicObject
which is a concrete implementation of IDynamicObject
.
public class DynamicObject : IDynamicObject
{
DynamicObjectMeta _metadata;
~DynamicObject()
{
DynamicObjectMeta._internal_data.Remove(this);
}
public MetaObject GetMetaObject(System.Linq.Expressions.Expression parameter)
{
return new DynamicObjectMeta(parameter, this);
}
}
As you can see, the dynamic object itself has nothing special defined; besides returning a new instance of a type DynamicObjectMeta
, and removing itself from a static Dictionary
within the DynamicObjectMeta
type, it is pretty barren. In fact, all the work of faking out the runtime happens in the DynamicObjectMeta
type, and most of it is essentially the same as the v-table approach we followed earlier. Again, for the instance level mutation to work, every instance must be uniquely represented in a dictionary which holds as its values, a dictionary of members associated with the given type. Here is a sample of how this can be done.
public class DynamicObjectMeta : MetaObject
{
internal static Dictionary<object,Dictionary<string, object>> _internal_data =
new Dictionary<object,Dictionary<string, object>>();
public DynamicObjectMeta(System.Linq.Expressions.Expression expression,
DynamicObject test) : base(expression,Restrictions.Empty,test)
{
}
public override MetaObject SetMember(SetMemberAction action, MetaObject[] args)
{
object key = args[0].Value;
if (_internal_data.ContainsKey(args[0].Value))
{
if (_internal_data[args[0].Value].ContainsKey(action.Name))
{
_internal_data[args[0].Value][action.Name] = args[0].Value;
}
else
{
_internal_data[args[0].Value].Add(action.Name, args[1].Value);
}
}
else
{
_internal_data.Add(args[0].Value, new Dictionary<string, object>());
_internal_data[args[0].Value].Add(action.Name, args[1].Value);
}
return this;
}
public override MetaObject GetMember(GetMemberAction action, MetaObject[] args)
{
MetaObject obj = new MetaObject(Expression.Constant(null), Restrictions.Empty);
if (_internal_data.ContainsKey(args[0].Value))
{
if (_internal_data[args[0].Value].ContainsKey(action.Name))
{
obj = new MetaObject(Expression.Constant(
_internal_data[args[0].Value][action.Name]),
Restrictions.Empty);
return obj;
}
}
return obj;
}
}
DynamicObjectMeta
inherits from MetaObject
, which provides the mechanism for intercepting the call resolution. In the sample above, I have overridden just two of the many possible virtual functions it provides, GetMember
and SetMember
. SetMember
is called whenever a value is set to a member that needs resolution. GetMember
is called whenever a value needs to be retrieved from a member that needs resolution. We all know at this point that a public property Hello
is not defined on DynamicObject
, so let us go ahead and see what happens when we run something like the following:
dynamic test = new DynamicObject();
test.Hello = "this is a test";
This will call the SetMember
function, passing Hello
as the SetMemberAction
value, object instance test
as the MetaObject
in index 0 of the MetaObject
array, and the string "this is a test" as the MetaObject
in index 1 of the MetaObject
array. This implementation simply takes those values and stores them in a static dictionary keyed on that object instance.
Any call to retrieve the value stored in test.Hello will automatically call the GetMember
method which simply reads that value from the Dictionary
(given the instance and method which are passed in exactly the same way). Here is how the creation and usage of the dynamic types will work once this approach is employed:
public static dynamic CreatePerson(string name, int age)
{
Action talk_method = () =>
{
Console.WriteLine("my name is {0}, I am {1} year(s) old ",name, age);
};
dynamic person = new DynamicObject();
person.Name = name;
person.Age = age;
person.Talk = talk_method;
return person;
}
As you can see from the above example, the methods and properties of Person
are applied completely dynamically. Neither Name
and Age
nor Talk
is part of the classification of the type. In fact, we have created a new Person
type from the generic DynamicObject
. Below is a listing to illustrate how this new person type can be mutated after creation.
dynamic oke = CreatePerson("oke", 4);
Action ride_bike_function = () =>
{
Console.WriteLine("Look Daddy!");
};
oke.RideBike = ride_bike_function;
Console.WriteLine("{0}\n{1}", oke.Name, oke.Age);
oke.Talk.Invoke();
dynamic azuka = CreatePerson("azuka", 2);
Action wear_diper_function = () =>
{
Console.WriteLine("It's good to be 2!");
};
azuka.WearDiper = wear_diper_function;
oke.RideBike.Invoke();
oke = null;
azuka.WearDiper.Invoke();
Just like in the JavaScript sample, the oke
instance of Person has been mutated with a RideBike
function, while the azuka
instance has been mutated with a WearDiper
. Both instances share talk, but have distinct unique defined behavior in their respective extra methods.
Conclusion
The infrastructure introduced in C# 4.0 to support dynamic types is quite robust. This article only begins to scratch the surface on what can be done with it. If you are interested in learning more, I suggest getting intimate with the inner workings of expression trees as they are crucial in processing dynamic calls. The samples above are rudimentary in nature. They neither fully utilize the appropriate feature set nor provide all the necessary checks and balances associated with building stable, well tested code. Please do not copy and paste them into any work verbatim, without further enhancements on your part to stabilize everything. Fair warning!