2011-06-27 3 views
7

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

  • Я не могу использовать собственный механизм привязки (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

Я, наконец, смог сделать это, используя связующее средство 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 
0

Вы можете использовать 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)); 

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

0

Если подкласс 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

Вещь, я работаю в предположении, что я не контролирую динамический объект, который я потребляю - это может быть любой объект, реализующий IDynamicMetaObjectProvider. –

0

Это в основном вариант этого вопроса: Dynamically adding members to a dynamic object, но вместо того, желая, чтобы добавить Вы желаете, чтобы заставить членов - так просто изменить вяжущего и подпись сайт вызова. Похоже, вы уже поняли, как сделать связующее.

1

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

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