Come rifletto sui membri dell'oggetto dinamico?


131

Devo ottenere un dizionario di proprietà e i loro valori da un oggetto dichiarato con la parola chiave dinamica in .NET 4? Sembra che usare la riflessione per questo non funzionerà.

Esempio:

dynamic s = new ExpandoObject();
s.Path = "/Home";
s.Name = "Home";

// How do I enumerate the Path and Name properties and get their values?
IDictionary<string, object> propertyValues = ???

Risposte:


32

Se IDynamicMetaObjectProvider è in grado di fornire i nomi dei membri dinamici, è possibile ottenerli. Vedere GetMemberNames implementazione nel Licenza Apache libreria PCL Dynamitey (che può essere trovato in NuGet), funziona per ExpandoObjects e DynamicObjects che implementano GetDynamicMemberNamese qualsiasi altra IDynamicMetaObjectProviderche fornisce un oggetto di meta con un'implementazione di GetDynamicMemberNamessenza personalizzati test al di là is IDynamicMetaObjectProvider.

Dopo aver ottenuto i nomi dei membri è un po 'più di lavoro per ottenere il valore nel modo giusto, ma Impromptu lo fa, ma è più difficile indicare solo i bit interessanti e avere un senso. Ecco la documentazione ed è uguale o più veloce della riflessione, tuttavia, è improbabile che sia più veloce di una ricerca nel dizionario per expando, ma funziona per qualsiasi oggetto, expando, dinamico o originale - lo chiami.


17
Grazie per arrivare al punto il codice è: var tTarget = target as IDynamicMetaObjectProvider; if (tTarget! = null) {tList.AddRange (tTarget.GetMetaObject (Expression.Constant (tTarget)). GetDynamicMemberNames ()); }
— Flatliner DOA

grazie per la tua grande biblioteca. Ho riscontrato problemi durante l'attivazione circolare di un oggetto dinamico in questa libreria dynamicjson.codeplex.com e sono stato in grado di estenderlo con la tua libreria.
— JJS,

1
esempio per favore.
— Demodave,

105

Nel caso di ExpandoObject, la classe ExpandoObject implementa effettivamente IDictionary<string, object>per le sue proprietà, quindi la soluzione è banale come il casting:

IDictionary<string, object> propertyValues = (IDictionary<string, object>)s;

Si noti che questo non funzionerà con oggetti dinamici generali. In questi casi dovrai passare al DLR tramite IDynamicMetaObjectProvider.


Grazie a ciò, purtroppo il campione è stato un po 'semplificato. Devo essere in grado di ispezionare un oggetto dinamico senza sapere quale sia il suo tipo reale.
— Flatliner DOA

2
Funziona solo con oggetti della classe ExpandoObject, la classe DynamicObject è un'altra classe estensibile che non implementa IDictionary ma implementa piuttosto IDynamicMetaObjectProvider.
— Sharique Abdullah,

1
Tuttavia risponde alla domanda del PO.
— Sharique Abdullah,

58

Esistono diversi scenari da considerare. Prima di tutto, devi controllare il tipo di oggetto. Puoi semplicemente chiamare GetType () per questo. Se il tipo non implementa IDynamicMetaObjectProvider, è possibile utilizzare la riflessione come per qualsiasi altro oggetto. Qualcosa di simile a:

var propertyInfo = test.GetType().GetProperties();

Tuttavia, per le implementazioni di IDynamicMetaObjectProvider, la semplice riflessione non funziona. Fondamentalmente, devi sapere di più su questo oggetto. Se si tratta di ExpandoObject (che è una delle implementazioni di IDynamicMetaObjectProvider), è possibile utilizzare la risposta fornita da itowlson. ExpandoObject memorizza le sue proprietà in un dizionario e puoi semplicemente trasmettere il tuo oggetto dinamico a un dizionario.

Se si tratta di DynamicObject (un'altra implementazione di IDynamicMetaObjectProvider), è necessario utilizzare tutti i metodi esposti da DynamicObject. DynamicObject non è necessario per "archiviare" effettivamente il suo elenco di proprietà ovunque. Ad esempio, potrebbe fare qualcosa del genere (sto riutilizzando un esempio dal mio post sul blog ):

public class SampleObject : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = binder.Name;
        return true;
    }
}

In questo caso, ogni volta che si tenta di accedere a una proprietà (con un determinato nome), l'oggetto restituisce semplicemente il nome della proprietà come stringa.

dynamic obj = new SampleObject();
Console.WriteLine(obj.SampleProperty);
//Prints "SampleProperty".

Quindi, non hai nulla su cui riflettere: questo oggetto non ha proprietà e allo stesso tempo tutti i nomi di proprietà validi funzioneranno.

Direi per le implementazioni di IDynamicMetaObjectProvider, è necessario filtrare le implementazioni note in cui è possibile ottenere un elenco di proprietà, come ExpandoObject, e ignorare (o generare un'eccezione) per il resto.


7
Mi sembra solo che se il tipo che consumo è dinamico, non dovrei fare ipotesi sul tipo sottostante. Dovrei essere in grado di chiamare GetDynamicMemberNames per ottenere l'elenco dei membri. Sembra stupido che i tipi statici supportino meglio l'ispezione di runtime rispetto a quelli dinamici!
— Flatliner DOA,

2
È possibile sovrascrivere il metodo GetDynamicMemberNames () per un oggetto dinamico per restituire i nomi dell'elenco dei membri dinamici. Il problema è che non è garantito che ogni oggetto dinamico abbia questo metodo (ExpandoObject no). Non sorprende che la riflessione funzioni meglio per i tipi statici. È stato creato per loro in primo luogo. Con la dinamica, devi fare più affidamento su unit test e TDD.
— Alexandra Rusina,

1
La documentazione MSDN per GetDynamicMemberNames () menziona: "Questo metodo esiste solo a scopo di debug.", Non molto confortante :(
— NicoJuicy,

19

Richiede Newtonsoft Json.Net

Un po 'in ritardo, ma ho pensato a questo. Ti dà solo i tasti e quindi puoi usarli sulla dinamica:

public List<string> GetPropertyKeysForDynamic(dynamic dynamicToGetPropertiesFor)
{
    JObject attributesAsJObject = dynamicToGetPropertiesFor;
    Dictionary<string, object> values = attributesAsJObject.ToObject<Dictionary<string, object>>();
    List<string> toReturn = new List<string>();
    foreach (string key in values.Keys)
    {
        toReturn.Add(key);                
    }
    return toReturn;
}

Quindi semplicemente foreach in questo modo:

foreach(string propertyName in GetPropertyKeysForDynamic(dynamicToGetPropertiesFor))
{
    dynamic/object/string propertyValue = dynamicToGetPropertiesFor[propertyName];
    // And
    dynamicToGetPropertiesFor[propertyName] = "Your Value"; // Or an object value
}

Scegliere di ottenere il valore come stringa o qualche altro oggetto, oppure eseguire un'altra dinamica e utilizzare nuovamente la ricerca.


Non è necessario l'elenco per tornare.
— aloisdg si trasferisce su codidact.com il

4
Perfetto! Ho dovuto modificare gli attributi JObjectAsJObject = dynamicToGetPropertiesFor; agli attributi ObjectAsJObject = (JObject) JToken.FromObject (obj); anche se!!
— k25,

Giusto per ribadire quanto detto da @ k25. È necessario sostituire JObject attributesAsJObject = dynamicToGetPropertiesFor;con qualcosa di simile: var jObject = (JObject) JToken.FromObject(dynamicToGetPropertiesFor);. A quel punto, puoi ottenere un dizionario di nomi e valori di proprietà facendo qualcosa di simile var objProperties = jObject.ToObject<Dictionary<string, object>>();. Con quello in mano, si parte per le gare. Questo non ha bisogno di una dinamica. Funziona bene con tutto ciò che è una sottoclasse diDynamicObject
— Flydog57,
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.