2013-02-11 13 views
7

Sto lavorando con expando object e sto tentando di definire una proprietà calcolata.Definire una proprietà calcolata su un oggetto expando

so che posso definire una semplice proprietà facendo qualcosa di simile al seguente:

dynamic myExpando = new ExpandoObject(); 
myExpando.TheAnswerToLifeTheUniverseAndEverything= 42; 

Allo stesso modo, posso anche definire un metodo:

myExpando.GetTheQuestion = ((Func<string>)(() => 
     { 
      return "How many road must a man walk down before we can call him a man?"; 
     })); 

Quando si lavora con un oggetto standard che può definire una proprietà calcolata, ovvero definire una proprietà che restituirà i risultati di un metodo/calc personalizzato. Non c'è bisogno di un esempio.

Ho bisogno di fare qualcosa di simile sul mio expando - avendo una proprietà che in realtà chiama un "Func" (o qualche altra forma di delegato, qualcosa va non appena posso chiamare un metodo personalizzato e avere un tipo di ritorno personalizzato) . Quindi in pratica ho bisogno di invocare un metodo come nel secondo esempio, ma farlo funzionare come una proprietà.

Fondamentalmente ho bisogno di essere in grado di chiamare con myExpando.GetTheQuestion invece di myExpando.GetTheQuestion(), pur mantenendo la capacità di definire un delegato personalizzato come il corpo immobile.

C'è un modo per farlo? Credo che I potrebbe fare ciò usando gli alberi di espressione, ma ammetto che sono un po 'perso lì. Qualcuno può fornire alcune indicazioni su come raggiungere questo obiettivo?


EDIT

Fatto qualche ricerca più .. A meno che non ci sia qualche molto specifica classe/interfaccia/sintassi che io non so che sto iniziando a pensare che quanto sopra è impossibile. Da quello che ottengo, la classe ExpandoObject funziona definendo alcuni metodi che eseguono il plumbing in background: TryGetMember, TrySetMember e così via. Ora quando "accede a una proprietà" sull'oggetto dinamico, TryGetMember è il membro che viene chiamato. Quel membro restituisce un valore da una sorta di dizionario interno (sì, lo so ... questo è un po 'semplificato ma dovrebbe dare l'idea) ... nessun test sul tipo di valore restituito. Ciò significa che nel mio esempio myExpando.GetTheQuestion restituirebbe il Func originale.

Sembrerebbe che dal momento che TryGetMember restituisca solo un valore, non c'è modo di farlo "eseguire" il codice di proprietà. Per ottenere ciò, è necessario un qualche tipo di espressione/lambda/func/azione surrogata il cui valore è in realtà il RISULTATO di un metodo. Il che sembra impossibile (e non avrebbe molto senso a meno che non mi manchi qualcosa - in pratica avresti un valore impostato su un "delegato" e quindi riceverai come valore di ritorno delegato ???). Sono corretto o questo o mi manca qualcosa?

risposta

2

È necessario rendere il proprio ExpandoObject, ereditando DynamicObject e prioritario

public override bool TryGetMember(GetMemberBinder binder, out object result) e public override bool TrySetMember(SetMemberBinder binder, object value)

Implementare TrySetMember per memorizzare il valore in una privata Dictionary<string,object> sotto binder.Name e utilizzare TryGetMember per recuperarla da quel dizionario, che ti darà un ExpandoObject di base.Quindi per assegnargli la funzionalità che ti serve, aggiungi un assegno in TryGetMember, dopo aver estratto l'oggetto, per vedere se è e poi usa il riflesso per vedere se non accetta alcun argomento. Se entrambi sono true, esegui il cast su dynamic e aggiungi nessuna parentesi di invocazione arg e assegnala a result.

public override bool TryGetMember(GetMemberBinder binder, out object result) 
{ 
     if (_dictionary.TryGetValue(binder.Name, out result)){ 
      if(result is Delegate && /* some reflection check on args*/){ 
       result = ((dynamic)result)(); 
      } 
     } 
} 

Ho un ImpromptuInterface framework open source (in NuGet) che ha un non sigillatoImpromptuDictionary che si potrebbe iniziare con il tuo ExpandoObject invece, soprattutto se avete bisogno di nessuna delle caratteristiche più sfumate di ExpandoObject tale come supporto vincolante alla GUI. Ha anche più dlr plumbing features che potresti trovare utile.

+0

Sì, in realtà ho pensato a una soluzione simile - ma mi chiedevo se questo dovrebbe essere possibile senza codice personalizzato (non che non potrei aggiungerlo - dopo tutto il mio expando object è già una classe ereditata personalizzata) usando espressione alberi e così via. Ad ogni modo, se nessun altro modo verrà pubblicato, accetterò questo come il miglior adattamento^_^ – SPArchaeologist

+0

Ok, per ora accetterò questo. È una soluzione alternativa, ma suppongo che sia anche l'unico modo per far sì che la "proprietà calcolata" si comporti come una proprietà normale. Dal momento che ho già esteso la base expando per supportare altri comportamenti (uso un expando sicuro che restituisce null per proprietà indefinite), immagino che potrei andare con questo (in realtà è stata la mia prima idea, ma ho postato la domanda comunque per vedere se mi mancava qualcosa). Grazie a jbtule, darò anche un'occhiata alla tua lib, c'è sempre qualcosa da imparare. – SPArchaeologist