I want to access a property on an object while leveraging the DLR binding mechanism.
dynamic
keyword in C#) because I do not know the property name at compile-time;IDictionary<string, object>
, to my knowledge, only solves the case of dynamic classes that choose to implement that interface (such as ExpandoObject
).Here is the demonstration code:
static void Main(string[] args)
{
dynamic obj = new System.Dynamic.ExpandoObject();
obj.Prop = "Value";
// C# dynamic binding.
Console.Out.WriteLine(obj.Prop);
// IDictionary<string, object>
Console.Out.WriteLine((obj as IDictionary<string, object>)["Prop"]);
// Attempt to use reflection.
PropertyInfo prop = obj.GetType().GetProperty("Prop");
Console.Out.WriteLine(prop.GetValue(obj, new object[] { }));
Console.In.ReadLine();
}
I was finally able to do it by using the C# runtime binder. (I believe it should be possible to do that with a simpler binder implementation, but I was unable to write a working one - probably writing a "simple" implementation is already quite tough).
using Microsoft.CSharp.RuntimeBinder;
private class TestClass
{
public String Prop { get; set; }
}
private static Func<object, object> BuildDynamicGetter(Type targetType, String propertyName)
{
var rootParam = Expression.Parameter(typeof(object));
var propBinder = Microsoft.CSharp.RuntimeBinder.Binder.GetMember(CSharpBinderFlags.None, propertyName, targetType, new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) });
DynamicExpression propGetExpression = Expression.Dynamic(propBinder, typeof(object),
Expression.Convert(rootParam, targetType));
Expression<Func<object, object>> getPropExpression = Expression.Lambda<Func<object, object>>(propGetExpression, rootParam);
return getPropExpression.Compile();
}
static void Main(string[] args)
{
dynamic root = new TestClass { Prop = "StatValue" };
Console.Out.WriteLine(root.Prop);
var dynGetter = BuildDynamicGetter(root.GetType(), "Prop");
Console.Out.WriteLine(dynGetter(root));
root = new System.Dynamic.ExpandoObject();
root.Prop = "ExpandoValue";
Console.WriteLine(BuildDynamicGetter(root.GetType(), "Prop").Invoke(root));
Console.Out.WriteLine(root.Prop);
Console.In.ReadLine();
}
Output:
StatValue
StatValue
ExpandoValue
ExpandoValue