2010-06-13 6 views
40

Ich bin auf der Suche nach einer Möglichkeit zum Ändern von Eigenschaften auf einem dynamic C# 4.0-Objekt mit dem Namen der Eigenschaft nur zur Laufzeit bekannt.Wie Sie eine Eigenschaft eines dynamischen C# 4-Objekts festlegen, wenn Sie den Namen in einer anderen Variablen haben

Gibt es eine Möglichkeit, wie etwas zu tun (ExpandoObject nur als ein Beispiel verwendet wird, könnte dies jede Klasse sein, die IDynamicMetaObjectProvider implementiert):

string key = "TestKey"; 
dynamic e = new ExpandoObject(); 
e[key] = "value"; 

, die gleich sein würde:

dynamic e = new ExpandoObject(); 
e.TestKey = "value"; 

Oder ist der einzige Weg nach vorne Reflexion?

+0

Reflection ist wahrscheinlich Ihre eigene Lösung, es sei denn, Sie fügen Ihre Eigenschaften in eine Hashmap ein, damit sie zur Laufzeit ermittelt werden können. –

+0

mögliches Duplikat von [Hinzufügen unbekannter (zur Entwurfszeit) Eigenschaften zu einem ExpandoObject] (http://stackoverflow.com/questions/2974008/adding-unknown-at-design-time-properties-to-an-expandoobject) – nawfal

Antwort

16

Nicht sehr einfach, nein. Reflektion funktioniert nicht, da es ein normales Typmodell annimmt, das nicht der volle Bereich von dynamic ist. Wenn Sie nur mit regulären Objekten sprechen, dann verwenden Sie einfach die Reflektion hier. Andernfalls erwarte ich, dass Sie den Code, den der Compiler für eine grundlegende Zuweisung ausgibt, rückentwickeln möchten, und optimieren Sie ihn, um einen flexiblen Mitgliedsnamen zu erhalten. Ich werde jedoch ehrlich sein: das ist keine attraktive Option; ein einfaches:

dynamic foo = ... 
foo.Bar = "abc"; 

übersetzt:

if (<Main>o__SiteContainer0.<>p__Site1 == null) 
{ 
    <Main>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, string, object>>.Create(Binder.SetMember(CSharpBinderFlags.None, "Bar", typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null) })); 
} 
<Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, foo, "abc"); 

Wenn Sie einen Ansatz wollen, die für dynamische und nicht-dynamischen Objekten funktioniert: FastMember für diese praktisch ist, und arbeitet entweder an der Typ oder Objektlevel:

// could be static or DLR 
var wrapped = ObjectAccessor.Create(obj); 
string propName = // something known only at runtime 
Console.WriteLine(wrapped[propName]); 

verfügbar auf Nuget, und stark optimiert für dynamische und nicht dynamische sc Enarios.

+0

Danke Marc - Ich dachte, ich hätte etwas offensichtlich übersehen, aber offensichtlich nicht! :) Hast du irgendwelche Links zu einem guten Beispiel dafür, wie man Code-Emission macht, damit ich das ausprobieren kann? Wenn ich es in Gang setze, sieht das aus wie etwas, das sich teilt! –

+0

@Kieran - es kommt darauf an, was Sie brauchen. Es hörte sich nicht so an, als ob du eine Emission benötigst (der Code, den ich gepostet habe, war nur ein Beispiel dafür, wie hässlich es ist). –

+0

Downvoted, da dies nicht erklärt, dass ein expandoObject ein Wörterbuch als Hintergrundspeicher verwendet und genau so verwendet werden kann, wie es das OP ursprünglich vorgeschlagen hat. Es gibt keine Notwendigkeit für die Reflexion oder Emission an einem expandoObject. Das heißt, dass @MarcGravell korrekt ist, dass für dynamische Objekte, die keine Expandos sind (sowie für reguläre Objekte, die einer dynamischen Variable zugewiesen sind), keine saubere Lösung existiert. –

3

Mein Open-Source-Framework Dynamitey verfügt über Methoden zum Aufrufen basierend auf String-Namen mit dem DLR. Es führt die Zwischenspeicherung von Bindungsstellen durch und rationalisiert sie auf einen Methodenaufruf. es läuft auch schneller als die Reflexion an nicht-dynamischen Objekten.

Dynamic.InvokeSet(e, "TestKey", "value"); 
50

Paul Sasik beantwortet eine ähnliche Frage an C# 4.0 Dynamic vs Expando... where do they fit?

using System; 
using System.Dynamic; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     dynamic expando = new ExpandoObject(); 
     var p = expando as IDictionary<String, object>; 
     p["A"] = "New val 1"; 
     p["B"] = "New val 2"; 

     Console.WriteLine(expando.A); 
     Console.WriteLine(expando.B); 
    } 
} 
0

fast-member kann die Rechnung passen - es sieht aus wie es die IL on the fly erzeugt, sondern speichert es so ist es wirklich schnell nach dem ersten Einsatz .

6

Um Jonas 'Antwort hinzuzufügen, müssen Sie keine neue Variable erstellen. Verwenden Sie stattdessen diesen Ansatz:

using System; 
using System.Dynamic; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     dynamic expando = new ExpandoObject(); 
     ((IDictionary<String, object>)expando)["A"] = "New val 1"; 
     ((IDictionary<String, object>)expando)["B"] = "New val 2"; 

     Console.WriteLine(expando.A); 
     Console.WriteLine(expando.B); 
    } 
} 
Verwandte Themen