Ottieni il valore della proprietà dinamica c # tramite stringa


182

Vorrei accedere al valore di una dynamicproprietà c # con una stringa:

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

Come posso ottenere il valore di d.value2 ("random") se ho solo "value2" come stringa? In javascript, potrei fare d ["value2"] per accedere al valore ("random"), ma non sono sicuro di come farlo con c # e reflection. Il più vicino che sono venuto è questo:

d.GetType().GetProperty("value2") ... ma non so come ottenerne il valore reale.

Come sempre, grazie per il tuo aiuto!


26
Si noti che questo non è lo scopo previsto di "dinamico" e che questo scenario non funziona meglio con "dinamico" rispetto a "oggetto". "dinamico" consente di accedere alle proprietà quando il nome della proprietà è noto al momento della compilazione ma il tipo non lo è. Dato che non conosci né il nome né il tipo al momento della compilazione, la dinamica non ti aiuterà.
Eric Lippert,


3
@EricLippert So che questa domanda è vecchia ma solo per fare un commento nel caso in cui qualcuno la veda in futuro. In alcuni casi non è possibile scegliere se utilizzare dinamico o oggetto (ad esempio quando si utilizza il parser JSON) e si potrebbe comunque voler ottenere le proprietà da una stringa (da un file di configurazione per esempio), quindi questo uso non è così insolito come si potrebbe pensare inizialmente.
Pedrom,

Risposte:


217

Una volta che hai il tuo PropertyInfo(da GetProperty), devi chiamare GetValuee passare l'istanza da cui vuoi ottenere il valore. Nel tuo caso:

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

4
Sto ottenendo un 'd.GetType().GetProperty("value2").GetValue(d)' threw an exception of type 'System.Reflection.TargetInvocationException' dynamic {System.Reflection.TargetInvocationException}nella finestra dell'orologio con quello ..?
TimDog

6
Pensa che GetValue abbia bisogno di un parametro aggiuntivo - egdGetType (). GetProperty ("value2"). GetValue (d, null)
dommer

3
Funzionerà su un vero ExpandoObject dinamico piuttosto che su un tipo anonimo? Poiché new {}crea un vero tipo anonimo con proprietà definite, ha senso chiamare GetType / GetProperty, ma per quanto riguarda ExpandoObject, che se chiami GetType, otterrai un tipo che ha le proprietà di ExpandoObject, ma non necessariamente le sue proprietà dinamiche.
Triynko,

16
-1. Funziona solo con semplici oggetti .NET che sono stati espressi in dinamici. Non funzionerà con nessun oggetto dinamico personalizzato come Expando o ViewBag utilizzato ASP.NET MVC
Philipp Munin

8
questo è ciò che funziona con Expando Object: (((IDictionary <stringa, oggetto>) 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);
}

Aggiungi riferimento a Microsoft.CSharp. Funziona anche con tipi dinamici e proprietà e campi privati.

Modifica : mentre questo approccio funziona, esiste un metodo quasi 20 volte più veloce dall'assembly Microsoft.VisualBasic.dll :

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

2
Volevo solo menzionare che la versione di VisualBasic non è equivalente alla versione originale di 'GetProperty' (GetProperty in realtà invoca il GetMember dinamico, che funziona anche su oggetti Python in IronPython).
Trevor Sundberg,

quale sarebbe l'oggetto target?
Demodave,

@Demodave L'oggetto su cui si desidera richiamare la proprietà ( dnella domanda).
IllidanS4 vuole che Monica ritorni il

This1 questo ha funzionato per proprietà private quando sia FastMember che HyperDescriptor no
Chris Marisic,

@ IllidanS4 quando hai confrontato il CallSitecodice contro il CallByNamecodice hai confrontato i due mentre hai memorizzato nella cache l' CallSiteistanza? Sospetto che il costo del tuo primo metodo sia quasi puramente l'attivazione di Bindere CallSitenon l'invocazione diTarget()
Chris Marisic,

24

Dynamitey è una .net stdlibreria open source , che ti permette di chiamarla come la dynamicparola chiave, ma usando la stringa a per il nome della proprietà piuttosto che il compilatore lo fa per te, e finisce per essere uguale alla riflessione in modo rapido (che non è altrettanto veloce come l'utilizzo della parola chiave dinamica, ma ciò è dovuto al sovraccarico aggiuntivo della memorizzazione dinamica della cache, in cui il compilatore memorizza nella cache staticamente).

Dynamic.InvokeGet(d,"value2");

11

Il metodo più semplice per ottenere sia a setterche a getterper una proprietà che funziona per qualsiasi tipo incluso dynamiced ExpandoObjectè quello di usarloFastMember che è anche il metodo più veloce in circolazione (usa Emit).

Puoi ottenere una TypeAccessorbase per un determinato tipo o una ObjectAccessorbase di un'istanza di un determinato tipo.

Esempio:

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

La maggior parte delle volte quando si richiede un oggetto dinamico, si ottiene un ExpandoObject (non nell'esempio precedente anonimo ma tipicamente statico della domanda, ma si menziona JavaScript e il mio parser JSON scelto JsonFx, per uno, genera ExpandoObjects).

Se la tua dinamica è in realtà un ExpandoObject, puoi evitare la riflessione inviandola a IDictionary, come descritto in http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx .

Dopo aver eseguito il cast su IDictionary, hai accesso a metodi utili come .Item e .ContainsKey


Sfortunatamente, dovendo eseguire il cast su IDictionary e utilizzare TryGetValue, ad esempio, viene restituito un vecchio oggetto semplice. Non è possibile sfruttare gli operatori impliciti a quel punto, poiché vengono considerati solo al momento della compilazione. Ad esempio, se avessi una classe Int64Proxy con conversione implicita in Int64?, Allora cercherei Int64? i = data.value; //data is ExpandoObjectautomaticamente e chiamerei l'operatore implicito. D'altra parte, se dovessi usare IDictionary per verificare se esiste un campo "valore", otterrei un oggetto che non eseguirà il cast senza errori su Int64 ?.
Triynko,

5

GetProperty / GetValue non funziona per i dati Json, genera sempre un'eccezione nulla, tuttavia è possibile provare questo approccio:

Serializza il tuo oggetto usando JsonConvert:

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

Quindi accedi direttamente riportandolo alla stringa:

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

Potrebbe funzionare direttamente applicando Convert.ToString (request) ["DynamicFieldName"], tuttavia non ho ancora testato.


2
Questo metodo genera l'errore: errore CS0021: impossibile applicare l'indicizzazione con [] a un'espressione di tipo "oggetto". Usa new JavaScriptSerializer().Deserialize<object>(json);per raggiungere le "proprietà" nel modo che hai suggerito
Kris Kilton,

4

d.GetType (). GetProperty ( "valore2")

restituisce un oggetto PropertyInfo.

Quindi, fallo

propertyInfo.GetValue(d)

2
grazie, questa era la risposta corretta, ma come detto sopra, GetValue(d)bisogna essereGetValue(d,null)
TimDog

4

Questo è il modo in cui ho ottenuto il valore di un valore di proprietà di un dinamico:

    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

Per ottenere proprietà dal documento dinamico quando .GetType()ritorna null, prova questo:

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

0

In .Net core 3.1 puoi provare così

d?.value2 , d?.value3

0

Simile alla risposta accettata, puoi anche provare GetFieldinvece di GetProperty.

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

A seconda di come è Typestato implementato l'effettivo , questo può funzionare quando GetProperty () non funziona e può anche essere più veloce.


FYI Differenza tra proprietà e campo in C # 3.0+: stackoverflow.com/a/653799/2680660
Efreeto
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.