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.