2012-12-05 24 views
18

Quando voglio chiamare dinamicamente un metodo staticamente definito ("staticamente" nel senso di "determinato in fase di compilazione", non nel senso di "membro di livello di classe") su qualsiasi oggetto in C#, posso usare riflessione per ottenere un manico a tale metodo ed invocano:Come posso chiamare dinamicamente un metodo su un oggetto dinamico?

typeof(Foo).GetMethod("Bar").Invoke(foo, new object[] { /* params */ }); 

Tuttavia, oggetti dinamizzare ereditando da DynamicObject rispondere a (undefined) metodo di richiamo di istanze utilizzando TryInvokeMember, ei metodi dinamici classe risponde non sono esposti attraverso la riflessione, per ovvi motivi. Ciò significa che non riesco a ottenere un handle di metodo per un metodo a cui dovrebbe essere risposto da TryInvokeMember.

Così, ironicamente, mi sembra che non si può chiamare in modo dinamico un metodo dinamico su un oggetto dynamic stessa facilità con cui è possibile chiamare un metodo definito su un oggetto non dynamic.

Ho considerato di chiamare direttamente TryInvokeMember, ma il primo argomento deve essere un'istanza di InvokeMemberBinder, una classe astratta. Sento che se devo implementare una classe per chiamare un metodo dinamico su un oggetto dinamico, devo fare qualcosa di sbagliato.

Come posso chiamare un metodo su un oggetto dynamic con il suo nome, sapendo che la classe di destinazione non non applicarla e che dovrebbe essere risposto a utilizzare TryInvokeMember?

risposta

9

Un modo per procedere è quello di simulare ciò che il compilatore C# emette per le invocazioni dei metodi sugli oggetti dinamici. Ciò richiede l'utilizzo di un gruppo di tipi contrassegnati con [EditorBrowsable(EditorBrowsableState.Never)] nello spazio dei nomi Microsoft.CSharp.RuntimeBinder, quindi non saranno visibili in Intellisense. Inutile dire che questo non sembra uno scenario supportato, quindi usalo a tuo rischio!

Questo codice chiama il metodo dinamico Bar senza argomenti su un'istanza di una classe derivata da DynamicObject:

dynamic dynamicObject = new DerivedFromDynamicObject(); 
var callSiteBinder = Binder.InvokeMember(CSharpBinderFlags.None, "Bar", Enumerable.Empty<Type>(), typeof(Program), 
    new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); 
var callSite = CallSite<Action<CallSite, object>>.Create(callSiteBinder); 
callSite.Target(callSite, dynamicObject); 

This post sul blog e this one avere dettagli più scabrosi su siti di chiamata e leganti.

+0

Suoni divertenti. Aspetterò di vedere se qualcuno ha effettivamente una soluzione supportata dal momento che probabilmente non sarà più in giro quando smetterà di funzionare. – zneak

+1

D'altra parte, dato che è quello che fa già il compilatore, è improbabile che smetta di funzionare, dato che ciò ucciderebbe qualsiasi applicazione usando 'dynamic' costruito oggi. – zneak

+0

@zneak Giusto, penserei che sia una scommessa abbastanza sicura. È semplicemente sorprendente che abbiano fatto di tutto per assicurarsi che i tipi siano nascosti. – Trillian

14

Ho un framework open source (licenza Apache) Dynamitey (disponibile in nuget) che incapsula il codice dinamico del raccoglitore, questo include il caching automatico dei siti di chiamata. Dispone di metodi convenienti per ogni tipo di raccoglitore (getter, setter, eventi, indicizzatori, operatori, conversioni), ma nello specifico si desidera InvokeMember.

Il codice del raccoglitore dinamico viene eseguito più rapidamente del riflesso (ammortizzato) quando si chiamano anche membri di classi definite staticamente (in fase di compilazione).

Dynamic.InvokeMember(foo,"Bar",arg...); 
+0

Sembra molto bello! – zneak

+0

Man! Grande nuget! Funziona perfettamente! Grazie! dovrebbe essere contrassegnato come risposta! – CodeHacker

+0

Ottimo pacchetto, il lavoro è fatto. – Andro