Search code examples
.netdynamic-language-runtime

Invoke a member of a dynamic object with a name defined at runtime in a String


I want to access a property on an object while leveraging the DLR binding mechanism.

  • I cannot use the native binding mechanism (dynamic keyword in C#) because I do not know the property name at compile-time;
  • I cannot use reflection because it only retrieves static type information;
  • casting to an 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();
    }

Solution

  • 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