The new dynamic keyword and the DLR in C# 4 and the .NET Framework 4.0 can be used for good, as displayed by IronRuby, IronPython and several other samples.
But it’s much more to fun to use it for evil… by making Reflection simple.
In C# 3.0 invoking members through reflection, was kind of odd and certainly not very readable.
var employee = new Employee(); var members = employee.GetType().GetMember("age", MemberTypes.All, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); ((FieldInfo)members[0]).SetValue(employee, 30);
By wrapping all reflection magic in a dynamic object the same call would look like:
var employee = (new Employee()).AsDynamic(); employee.Name = "Paul van Brenk"; employee.Age = 30; Console.WriteLine("Employee {0} is {1} years old.", employee.Name, employee.Age);
How this works is relatively easy.. by deriving a wrapper class from the new DynamicObject and overriding the TrySetMember and TryGetMember object to do the dirty work for you.
static class DynamicHelper { public static dynamic AsDynamic(this T source) { return new DynamicReflection(source); } class DynamicReflection : DynamicObject { public DynamicReflection(T source) : base() { this.Source = source; } public override bool TrySetMember(SetMemberBinder binder, object value) { // find the member MemberInfo member; if (!TryFindMember(binder.Name, out member)) { return false; } // we can only set values to fields and properties // using reflection switch (member.MemberType) { /* todo: check the type of the incoming value and the type of the property. */ case (MemberTypes.Field): ((FieldInfo)member).SetValue(Source, value); return true; case (MemberTypes.Property): ((PropertyInfo)member).SetValue(Source, value,/*ndex*/ null); // we don't support indexed properties return true; } // didn't work return false; } public override bool TryGetMember(GetMemberBinder binder, out object result) { // find the member MemberInfo member; if (!TryFindMember(binder.Name, out member)) { result = null; return false; } // we can only set values to fields and properties // using reflection switch (member.MemberType) { /* todo: check the type of the incoming value and the type of the property. */ case (MemberTypes.Field): result = ((FieldInfo)member).GetValue(Source); return true; case (MemberTypes.Property): result = ((PropertyInfo)member).GetValue(Source,/*ndex*/ null); // we don't support indexed properties return true; } // didn't work result = null; return false; } private bool TryFindMember(string name, out MemberInfo memberInfo) { // find the member var members = Type.GetMember(name, MemberTypes.All, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); // more than 1 not supported for now if (members.Length != 1) { memberInfo = null; return false; } memberInfo = members[0]; return true; } private Type Type { get { return typeof(T); } } public T Source { get; private set; } } }
More info about implementing dynamic objects and behavior can be found on the DLR CodePlex site (esp. Getting Started with the DLR as a Library Author).
Source: Program.cs.txt