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.