Ruft den Wert der dynamischen Eigenschaft c # über eine Zeichenfolge ab


180

Ich möchte dynamicmit einer Zeichenfolge auf den Wert einer c # -Eigenschaft zugreifen :

dynamic d = new { value1 = "some", value2 = "random", value3 = "value" };

Wie kann ich den Wert von d.value2 ("random") erhalten, wenn ich nur "value2" als Zeichenfolge habe? In Javascript könnte ich d ["value2"] tun, um auf den Wert ("random") zuzugreifen, aber ich bin mir nicht sicher, wie ich das mit c # und Reflection machen soll. Am nächsten bin ich gekommen:

d.GetType().GetProperty("value2") ... aber ich weiß nicht, wie ich den tatsächlichen Wert daraus ziehen kann.

Wie immer vielen Dank für Ihre Hilfe!


26
Beachten Sie, dass dies nicht der beabsichtigte Zweck von "dynamisch" ist und dass dieses Szenario mit "dynamisch" nicht besser funktioniert als mit "Objekt". "dynamisch" ermöglicht den Zugriff auf Eigenschaften, wenn der Name der Eigenschaft zur Kompilierungszeit bekannt ist, der Typ jedoch nicht. Da Sie beim Kompilieren weder den Namen noch den Typ kennen, hilft Ihnen Dynamic nicht weiter.
Eric Lippert


3
@EricLippert Ich weiß, dass diese Frage alt ist, aber nur um einen Kommentar abzugeben, falls jemand sie in Zukunft sehen sollte. In einigen Fällen können Sie nicht auswählen, ob Sie Dynamic oder Object verwenden möchten (z. B. bei Verwendung des JSON-Parsers), und Sie möchten die Eigenschaften möglicherweise dennoch aus einer Zeichenfolge (z. B. aus einer Konfigurationsdatei) abrufen, sodass diese Verwendung nicht ungewöhnlich ist wie man zunächst denken könnte.
Pedrom

Antworten:


216

Sobald Sie Ihr PropertyInfo(von GetProperty) haben, müssen Sie GetValuedie Instanz aufrufen und übergeben, von der Sie den Wert erhalten möchten. In deinem Fall:

d.GetType().GetProperty("value2").GetValue(d, null);

4
Ich bekomme damit ein 'd.GetType().GetProperty("value2").GetValue(d)' threw an exception of type 'System.Reflection.TargetInvocationException' dynamic {System.Reflection.TargetInvocationException}im Uhrenfenster ..?
TimDog

6
Denken Sie, GetValue benötigt einen zusätzlichen Parameter - egdGetType (). GetProperty ("value2"). GetValue (d, null)
Dommer

3
Funktioniert dies auf einem echten dynamischen ExpandoObject und nicht auf einem anonymen Typ? Da new {}ein wirklich anonymer Typ mit definierten Eigenschaften erstellt wird, ist das Aufrufen von GetType / GetProperty sinnvoll. Was ist jedoch mit ExpandoObject. Wenn Sie GetType aufrufen, erhalten Sie einen Typ, der die Eigenschaften von ExpandoObject aufweist, jedoch nicht unbedingt die dynamischen Eigenschaften.
Triynko

16
-1. Dies funktioniert nur mit einfachen .NET-Objekten, die in dynamisch umgewandelt wurden. Es funktioniert nicht mit benutzerdefinierten dynamischen Objekten wie Expando oder ViewBag, die ASP.NET MVC
Philipp Munin am

8
Dies ist, was mit Expando Object funktioniert: (((IDictionary <string, object>) x)) ["value1"]
Michael Bahig

39
public static object GetProperty(object target, string name)
{
    var site = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, name, target.GetType(), new[]{Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0,null)}));
    return site.Target(site, target);
}

Verweis auf Microsoft.CSharp hinzufügen. Funktioniert auch für dynamische Typen und private Eigenschaften und Felder.

Bearbeiten : Während dieser Ansatz funktioniert, gibt es eine fast 20-mal schnellere Methode aus der Microsoft.VisualBasic.dll- Assembly:

public static object GetProperty(object target, string name)
{
    return Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(target, name, CallType.Get);
}

2
Ich wollte nur erwähnen, dass die VisualBasic-Version nicht Ihrer ursprünglichen 'GetProperty'-Version entspricht (die GetProperty ruft tatsächlich das dynamische GetMember auf, das sogar für Python-Objekte in IronPython funktioniert).
Trevor Sundberg

Was wäre das Objektziel?
Demodave

@Demodave Das Objekt, für das Sie die Eigenschaft aufrufen möchten ( din der Frage).
IllidanS4 will Monica am

➕1 Dies funktionierte für private Immobilien, wenn sowohl FastMember als auch HyperDescriptor dies nicht
taten

@ IllidanS4 Als Sie den CallSiteCode mit dem Code verglichen haben, CallByNamehaben Sie die beiden beim Zwischenspeichern der CallSiteInstanz verglichen ? Ich würde vermuten, dass die Kosten Ihrer ersten Methode fast nur die Aktivierung des Binderund CallSitenicht die Anrufung vonTarget()
Chris Marisic

24

Dynamitey ist eine Open-Source- .net stdBibliothek, die Sie wie das dynamicSchlüsselwort aufrufen können. Verwenden Sie jedoch eine Zeichenfolge für den Eigenschaftsnamen und nicht den Compiler, der dies für Sie erledigt. Am Ende entspricht dies der Reflexionsgeschwindigkeit (was bei weitem nicht so schnell ist) Dies ist jedoch auf den zusätzlichen Aufwand für das dynamische Caching zurückzuführen, bei dem der Compiler statisch zwischenspeichert.

Dynamic.InvokeGet(d,"value2");

11

Die einfachste Methode, um sowohl a setterals auch a getterfür eine Eigenschaft zu erhalten, die für jeden Typ funktioniert, einschließlich dynamicund ExpandoObjectzu verwenden FastMember, ist auch die schnellste Methode (es wird Emit verwendet).

Sie können entweder eine TypeAccessorbasierend auf einem bestimmten Typ oder eine ObjectAccessorbasierend auf einer Instanz eines bestimmten Typs erhalten.

Beispiel:

var staticData = new Test { Id = 1, Name = "France" };
var objAccessor = ObjectAccessor.Create(staticData);
objAccessor["Id"].Should().Be(1);
objAccessor["Name"].Should().Be("France");

var anonymous = new { Id = 2, Name = "Hilton" };
objAccessor = ObjectAccessor.Create(anonymous);
objAccessor["Id"].Should().Be(2);
objAccessor["Name"].Should().Be("Hilton");

dynamic expando = new ExpandoObject();
expando.Id = 3;
expando.Name = "Monica";
objAccessor = ObjectAccessor.Create(expando);
objAccessor["Id"].Should().Be(3);
objAccessor["Name"].Should().Be("Monica");

var typeAccessor = TypeAccessor.Create(staticData.GetType());
typeAccessor[staticData, "Id"].Should().Be(1);
typeAccessor[staticData, "Name"].Should().Be("France");

typeAccessor = TypeAccessor.Create(anonymous.GetType());
typeAccessor[anonymous, "Id"].Should().Be(2);
typeAccessor[anonymous, "Name"].Should().Be("Hilton");

typeAccessor = TypeAccessor.Create(expando.GetType());
((int)typeAccessor[expando, "Id"]).Should().Be(3);
((string)typeAccessor[expando, "Name"]).Should().Be("Monica");

8

Wenn Sie nach einem dynamischen Objekt fragen, erhalten Sie häufig ein ExpandoObject (nicht im anonymen, aber statisch typisierten Beispiel der Frage oben, aber Sie erwähnen JavaScript und mein ausgewählter JSON-Parser JsonFx generiert ExpandoObjects).

Wenn Ihre Dynamik tatsächlich ein ExpandoObject ist, können Sie Reflexionen vermeiden, indem Sie sie in IDictionary umwandeln, wie unter http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx beschrieben .

Sobald Sie in IDictionary umgewandelt haben, haben Sie Zugriff auf nützliche Methoden wie .Item und .ContainsKey


Wenn Sie beispielsweise in IDictionary umwandeln und beispielsweise TryGetValue verwenden müssen, wird ein einfaches altes Objekt zurückgegeben. Sie können implizite Operatoren zu diesem Zeitpunkt nicht nutzen, da sie nur zur Kompilierungszeit berücksichtigt werden. Wenn ich beispielsweise eine Int64Proxy-Klasse mit impliziter Konvertierung in Int64? Hätte, Int64? i = data.value; //data is ExpandoObjectwürde der implizite Operator automatisch gesucht und aufgerufen. Wenn ich andererseits IDictionary verwenden müsste, um zu testen, ob das Feld "Wert" vorhanden ist, würde ich ein Objekt zurückerhalten, das nicht fehlerfrei in Int64 umgewandelt wird.
Triynko

5

GetProperty / GetValue funktioniert nicht für Json-Daten. Es wird immer eine Null-Ausnahme generiert. Sie können jedoch diesen Ansatz ausprobieren:

Serialisieren Sie Ihr Objekt mit JsonConvert:

var z = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(request));

Greifen Sie dann direkt darauf zu und werfen Sie es zurück in string:

var pn = (string)z["DynamicFieldName"];

Es kann direkt funktionieren, wenn Convert.ToString (Anfrage) ["DynamicFieldName"] angewendet wird, ich habe es jedoch nicht getestet.


2
Diese Methode generiert den Fehler: error CS0021: Indizierung mit [] kann nicht auf einen Ausdruck vom Typ 'Objekt' angewendet werden. Verwenden Sie new JavaScriptSerializer().Deserialize<object>(json);, um zu den "Eigenschaften" in der von Ihnen vorgeschlagenen Weise zu gelangen
Kris Kilton

4

d.GetType (). GetProperty ("value2")

Gibt ein PropertyInfo-Objekt zurück.

Also dann tu es

propertyInfo.GetValue(d)

2
danke, das war die richtige Antwort, aber wie oben erwähnt, GetValue(d)muss das seinGetValue(d,null)
TimDog

4

Auf diese Weise habe ich den Wert eines Eigenschaftswerts einer Dinamic erhalten:

    public dynamic Post(dynamic value)
    {            
        try
        {
            if (value != null)
            {
                var valorCampos = "";

                foreach (Newtonsoft.Json.Linq.JProperty item in value)
                {
                    if (item.Name == "valorCampo")//property name
                        valorCampos = item.Value.ToString();
                }                                           

            }
        }
        catch (Exception ex)
        {

        }


    }

1

Versuchen Sie .GetType()Folgendes null, um Eigenschaften aus dem dynamischen Dokument bei der Rückgabe abzurufen:

var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;

0

In .Net Core 3.1 können Sie dies versuchen

d?.value2 , d?.value3

0

Ähnlich wie bei der akzeptierten Antwort können Sie auch versuchen, GetFieldanstatt GetProperty.

d.GetType().GetField("value2").GetValue(d);

Abhängig davon, wie das tatsächliche Typeimplementiert wurde, kann dies funktionieren, wenn GetProperty () dies nicht tut und sogar schneller sein kann.


Zu Ihrer Information
Efreeto
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.