2011-06-27 17 views
7

Chcę uzyskać dostęp do właściwości obiektu podczas korzystania z mechanizmu powiązania DLR.Wywołanie elementu obiektu dynamicznego o nazwie zdefiniowanej w środowisku wykonawczym w ciągu znaków

  • Nie mogę użyć macierzystego mechanizmu wiążącego (dynamic słowo kluczowe w języku C#), ponieważ nie znam nazwy właściwości podczas kompilacji;
  • Nie mogę użyć odbicia, ponieważ pobiera on tylko informacje o typie statycznym;
  • odlewanie do IDictionary<string, object>, według mojej wiedzy, rozwiązuje tylko przypadek klas dynamicznych, które decydują się na implementację tego interfejsu (np. ExpandoObject).

Oto kod demonstracji:

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

Odpowiedz

5

Byłem w stanie to zrobić za pomocą spoiwa runtime C#. (Sądzę, że powinno być to możliwe przy prostszej implementacji spinacza, ale nie byłem w stanie napisać działającego - prawdopodobnie napisanie "prostej" implementacji jest już dość trudne).

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

wyjściowa:

StatValue 
StatValue 
ExpandoValue 
ExpandoValue 
0

Można użyć C# dynamic z kompilacji wykonawczego w celu dostosowania wymogu nieznanej nazwie.

Przykład:

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

To działa, ale wątpię, że jest to najlepsza opcja i jeśli trzeba wywołać metodę może się nieco bardziej skomplikowane.

0

Jeśli podklasy DynamicObject można zastąpić TrySetMember przechowywać właściwości nazwy w lokalnym słowniku, a zastępują TryGetIndex aby umożliwić pobieranie ich za pomocą nazwy właściwości jako ciąg znaków. Jest to całkiem niezły przykład na stronie MSDN na stronie DynamicObject.

To nie jest najszybszy sposób, aby to zrobić, ponieważ nie korzysta z dynamicznej wysyłki i wywołań (i innych rzeczy, których nie rozumiem), jak również może, ale zadziała. To kończy się wyglądać tak:

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

Rzecz w tym, że pracuję przy założeniu, że nie kontrolują obiekt dynamiczny, że jestem czasochłonne - może to być dowolny obiekt, który implementuje IDynamicMetaObjectProvider. –

0

Jest to po prostu wariant to pytanie: Dynamically adding members to a dynamic object ale zamiast chcąc dodać jesteś chcąc dostać członków - tak po prostu zmienić spoiwo i podpis strona połączenia. Wygląda na to, że już wiesz, jak zrobić segregator.

1

Zostanie to otwarte środowisko katalogowe ImpromptuInterface (dostępne przez nuget). Używa tych samych wywołań api, co kompilator C#, działa szybciej niż odbicie na obiektach nie dynamicznych.

Console.Out.WriteLine(Impromptu.InvokeGet(obj,"Prop")); 
Powiązane problemy