8

Sto cercando un modo per aggiungere membri dinamicamente a un oggetto dinamico. OK, credo che sia necessario un piccolo chiarimento ...Aggiunta dinamica di membri a un oggetto dinamico

Quando lo fate:

dynamic foo = new ExpandoObject(); 
foo.Bar = 42; 

La proprietà Bar saranno aggiunti in modo dinamico in fase di esecuzione. Ma il codice si riferisce ancora "staticamente" a Bar (il nome "Bar" è hardcoded) ... E se volessi aggiungere una proprietà in runtime senza conoscerne il nome in fase di compilazione?

so come fare questo con un oggetto dinamico personalizzato (in realtà ho blogged about it qualche mese fa), utilizzando i metodi della classe DynamicObject, ma come posso farlo con qualsiasi oggetto dinamico?

Probabilmente potrei usare l'interfaccia IDynamicMetaObjectProvider, ma non capisco come usarlo. Ad esempio, quale argomento dovrei passare al metodo GetMetaObject? (si aspetta un Expression)

E a proposito, come si esegue la riflessione sugli oggetti dinamici? Riflessione "normale" e TypeDescriptor non mostrare i membri dinamici ...

Qualsiasi intuizione sarebbe apprezzata!

+0

In C# 6.0, * potrebbe essere * puoi scriverlo come 'foo. $ Bar = 42;' :) Non sono sicuro se è consentito per dinamici ... – nawfal

+0

@nawfal, in realtà, quella funzione è stata abbandonata .. ma comunque, 'foo. $ Bar' è solo una scorciatoia per' foo ["Bar"] ' –

+0

Thomas, non sapevo che la funzione sarebbe stata lasciata cadere (sono contento a riguardo), ma oh sì, per un momento Ho trascurato il vero requisito del tuo q. – nawfal

risposta

9

Ciò che si desidera è simile alle funzioni getattr/setattr di Python. Non esiste un modo equivalente per farlo in C# o VB.NET. Lo strato esterno del DLR (che include w/IronPython e IronRuby in Microsoft.Scripting.dll) include un set di API di hosting che include un'API ObjectOperations con metodi GetMember/SetMember. Potresti usarli, ma avresti bisogno della dipendenza extra del DLR e di un linguaggio basato su DLR.

Probabilmente l'approccio più semplice sarebbe creare un CallSite con uno dei raccoglitori C# esistenti. Puoi ottenere il codice per questo osservando il risultato di "foo.Bar = 42" in ildasm o reflector. Ma un semplice esempio di questo sarebbe:

object x = new ExpandoObject(); 
CallSite<Func<CallSite, object, object, object>> site = CallSite<Func<CallSite, object, object, object>>.Create(
      Binder.SetMember(
       Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags.None, 
       "Foo", 
       null, 
       new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) } 
      ) 
     ); 
site.Target(site, x, 42); 
Console.WriteLine(((dynamic)x).Foo); 
+0

Ho bisogno di un po 'di tempo per assicurarmi di capire veramente cosa stia facendo questo codice, ma comunque sta funzionando bene ... Grazie! –

+0

Sarebbe possibile fare questo <.Net 4.0 usando il DLR? Sembra che l'uso della dinamica precluderebbe questo. – Firestrand

+0

Il metodo sopra non dovrebbe funzionare correttamente. sito di chiamata Setter richiede 2 informazioni su argomenti da fornire invece di 1. La definizione corretta sarà: nuovo [] { CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.None, nullo), CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.None, nullo) } –

5

Il quadro opensource Dynamitey farà questo (disponibile tramite nuget). Incapsula mentre memorizza nella cache il sito di chiamata e il codice di binder che @ Dino-Viehland utilizzava.

Dynamic.InvokeSet(foo,"Bar",42); 

Può anche chiamare molti other kinds of c# binder too.

5

ExpandoObject implementa IDictionary < string, oggetto > anche se esplicitamente. Ciò significa che puoi semplicemente lanciare la stringa ExpandoObject in IDictionary <, l'oggetto > e manipolare il dizionario.

dynamic foo = new ExpandoObject(); 
foo.Bar = 42; 
food = (IDictionary<string,object>)foo; 
food["Baz"] = 54 
+0

Grazie! Infatti ExpandoObject implementa IDictionary, come ho capito in seguito, quindi è chiaramente la soluzione più semplice in quel caso. Comunque la portata della mia domanda era più ampia: stavo cercando una soluzione che potesse funzionare con qualsiasi oggetto dinamico, non solo ExpandoObject –

1

So che questo è piuttosto un vecchio post, ma ho pensato di passare lungo Miron Abramson solution su come è possibile creare il proprio tipo e aggiungi le proprietà in fase di esecuzione - nel caso in cui nessun altro là fuori è alla ricerca di qualcosa di simile.