Jean Hominal
Jean Hominal

Reputation: 16796

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.

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();
    }

Upvotes: 9

Views: 3115

Answers (5)

jbtule
jbtule

Reputation: 31809

The opensource framework dynamitey will do this (available via nuget). It uses that same api calls as the c# compiler.

Console.Out.WriteLine(Dynamic.InvokeGet(obj,"Prop"));

Upvotes: 3

Jean Hominal
Jean Hominal

Reputation: 16796

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

Upvotes: 6

Dino Viehland
Dino Viehland

Reputation: 6486

This is basically a variant of this question: Dynamically adding members to a dynamic object but instead of wanting to add you're wanting to get the members - so just change the binder and the signature of the call site. It looks like you've already figured out how to make the binder.

Upvotes: 0

Steve Wilkes
Steve Wilkes

Reputation: 7135

If you subclass DynamicObject you can override TrySetMember to store the properties by name in a local dictionary, and override TryGetIndex to enable retrieving them using the property name as a string. There's a pretty good example of this at the DynamicObject page on MSDN.

This is not the fastest way to do it as it doesn't take advantage of dynamic dispatch and callsites (and other stuff I don't quite understand) as well as it could, but it will work. It'll end up looking like this:

dynamic myDynamicClass = new MyDynamic();

// Uses your TrySetMember() override to add "Value" to the internal dictionary against the key "Prop":
myDynamicClass.Prop = "Value";

// Uses your TryGetIndex override to return the value of the internal dictionary entry for "Prop":
object value = myDynamicClass["Prop"];

Upvotes: 0

Jo&#227;o Angelo
Jo&#227;o Angelo

Reputation: 57718

You could use C# dynamic with runtime compilation to accommodate the requirement of the unknown property name.

An example:

dynamic d = new ExpandoObject();

d.Value = 7;

var helper = "" +
    "using System; " +
    "public class Evaluator " + 
    "{{ " + 
    "    public object Eval(dynamic d) {{ return d.{0}; }} " + 
    "}}";

var references = new string[] 
{ 
    "System.dll", 
    "System.Core.dll", 
    "Microsoft.CSharp.dll" 
};

var parameters = new CompilerParameters(references, "Test.dll");
var compiler = new CSharpCodeProvider();

var results = compiler.CompileAssemblyFromSource(
    parameters,
    String.Format(helper, "Value"));

dynamic exp = Activator.CreateInstance(
    results.CompiledAssembly.GetType("Evaluator"));

Console.WriteLine(exp.Eval(d));

This works, but I doubt that this is the best option and if you need invoke method it can get a bit more complex.

Upvotes: 0

Related Questions