Вызвать член динамического объекта с именем, определенным во время выполнения в строке - PullRequest
8 голосов
/ 27 июня 2011

Я хочу получить доступ к свойству объекта, используя механизм привязки DLR.

  • Я не могу использовать собственный механизм привязки (ключевое слово dynamic в C #), поскольку не знаю свойствоимя во время компиляции;
  • Я не могу использовать отражение, потому что оно только извлекает информацию статического типа;
  • приведение к IDictionary<string, object>, насколько мне известно, решает только случай динамических классов, которые решили реализовать этот интерфейс (например, ExpandoObject).

Вотдемонстрационный код:

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

Ответы [ 5 ]

6 голосов
/ 27 июня 2011

Наконец-то я смог сделать это, используя механизм связывания C #.(Я полагаю, что это возможно сделать с помощью более простой реализации связующего, но я не смог написать рабочую - вероятно, написать «простую» реализацию уже довольно сложно).

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

Вывод:

StatValue
StatValue
ExpandoValue
ExpandoValue
1 голос
/ 06 июля 2011

Платформа с открытым исходным кодом ImpromptuInterface сделает это (доступно через nuget ). Он использует те же вызовы API, что и компилятор c #, и работает быстрее, чем отражение в не динамических объектах.

Console.Out.WriteLine(Impromptu.InvokeGet(obj,"Prop"));
0 голосов
/ 28 июня 2011

Это в основном вариант этого вопроса: Динамическое добавление членов к динамическому объекту , но вместо того, чтобы добавлять, вы хотите получить члены - так что просто измените подшивку и подпись позвоните на сайт. Похоже, вы уже поняли, как сделать связующее.

0 голосов
/ 27 июня 2011

Если вы используете подкласс DynamicObject, вы можете переопределить TrySetMember , чтобы сохранить свойства по имени в локальном словаре, и переопределить TryGetIndex , чтобы разрешить их извлечение с использованием имени свойства в виде строки , На странице DynamicObject в MSDN есть довольно хороший пример.

Это не самый быстрый способ сделать это, так как он не использует преимущества динамической отправки и рабочих мест (и других вещей, которые я не совсем понимаю), так как это возможно, но это будет работать. В конечном итоге это будет выглядеть так:

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"];
0 голосов
/ 27 июня 2011

Вы можете использовать C # dynamic с компиляцией во время выполнения, чтобы удовлетворить требование неизвестного имени свойства.

Пример:

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

Это работает, но я сомневаюсь, что это лучший вариант, и если вам нужен метод invoke, он может стать немного сложнее.

...