“Dynamic” keyword was introduced in .net framework 4.0. Recently I got a chance to explore dlr and c# dynamics in more depth and I think its magic.
In this post I am going to show you some of the interesting things you can do with dynamics in c#
if you want to read more about .net Dynamics and DLR in general you can do that here:
http://visualstudiomagazine.com/articles/2011/02/01/understanding-the-dynamic-keyword-in-c4.aspx
Ok, assuming you have read up a bit on it, or kinda know it already, Lets Start With a Simple Class
public class Person { public Person()
{
Name = "Preet";
}
public string Name { get; set; }
}
The class above has only one property Name, We should be able to add more functionality to this class.
We should be able to do something like this:
dynamic person = DynamicDecorator<Person>.GetDecorated(new Person());
person.Age = 35;
person.AgeFormatter = new Func<dynamic, string> (p => string.Format("{0} your age is {1}", p.Name, p.Age));
Console.WriteLine(person.Name);
Console.WriteLine(person.Age);
Console.WriteLine(person.AgeFormatter(person));
person.SetInterceptor("AgeFormatter", new Dummy());
Console.WriteLine(person.AgeFormatter(person));
Console.ReadKey();
For this we need some form of decorator, we can use to add functionality/behavior to this class:
public class DynamicDecorator<T> : DynamicObject
{
/// <summary>
/// This is the object we are decorating or going to add properties to it
/// </summary>
public T ObjectToDecorate { get; set; }
/// <summary>
/// A bag where we put all the existing and new properties
/// </summary>
private Dictionary<string, object> _properties;
/// <summary>
/// A bag where we store all our interceptors or may be method call forwarders
///(*** This needs to be a bit better***)
/// </summary>
private Dictionary<string, object> _interceptors;
/// <summary>
/// As we are decorating a static class we need a constructor that takes in a type,
///so we can determine existing properties and methods
/// </summary>
/// <param name="objectToDecorate"></param>
private DynamicDecorator(T objectToDecorate)
{
ObjectToDecorate = objectToDecorate;
_properties = new Dictionary<string, object>();
_interceptors = new Dictionary<string, object>();
AddExistingProperties();
}
/// <summary>
/// This goes through the existing properties and adds them to the bag
/// </summary>
private void AddExistingProperties()
{
var existingProperties = ObjectToDecorate.GetType().GetProperties().ToList();
foreach (var existingProperty in existingProperties)
{
_properties.Add(existingProperty.Name, existingProperty.GetValue(ObjectToDecorate));
}
}
/// <summary>
/// Set all the interceptors
/// </summary>
/// <param name="memberToIntercept"></param>
/// <param name="d"></param>
public void SetInterceptor(string memberToIntercept, object d)
{
if (_interceptors.ContainsKey(memberToIntercept))
{
_interceptors[memberToIntercept] = d;
return;
}
_interceptors.Add(memberToIntercept, d);
}
/// <summary>
/// Get the original item as dynamic so we can bolt on extra stuff on it
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
public static dynamic GetDecorated(T person)
{
return new DynamicDecorator<T>(person);
}
/// <summary>
/// This is whhat gets executed when you try to get a member on the decorated type
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return _properties.TryGetValue(binder.Name, out result);
}
/// <summary>
/// When you try to add a new member to the decorated type, this is what gets executed
/// </summary>
/// <param name="binder"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (_properties.ContainsKey(binder.Name))
{
_properties[binder.Name] = value;
}
_properties.Add(binder.Name, value);
return true;
}
/// <summary>
/// This is what happens when you try to invoke a member on the Decorated type
/// </summary>
/// <param name="binder"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
// check if the interceptor has been set for this member
if (_interceptors.ContainsKey(binder.Name))
{
var toInvoke = _interceptors[binder.Name];
var method = toInvoke.GetType().GetMethods()
.FirstOrDefault(info =>
info.Name.Contains(binder.Name));
result = method.Invoke(toInvoke, args);
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
}
This produces the following output:
Now for the explanation of the events: