2009-06-07 11 views
6

Qualcuno sa di un modo per intercettare le chiamate al metodo dynamic (in particolare quelle che stanno per aumentare RuntimeBinderException s) con un RealProxy? Speravo di cogliere l'eccezione e di implementare il "metodo mancante", ma sembra che venga gettato prima che l'intercettatore riesca a dare un'occhiata.Difficoltà mancate nel metodo in C# 4.0: dinamico vs RealProxy

Il mio test sembra proprio:

dynamic hello = MethodMissingInterceptor<DynamicObject>.Create(); 
Assert.AreEqual("World", hello.World()); 

Dove World non è effettivamente attuate sulla DynamicObject. L'intercettore è piuttosto semplice - Speravo di controllare IMethodReturnMessage.Exception per RuntimeBinderException e in avanti a qualcosa di simile:

public IMessage MethodMissing(IMethodCallMessage call) 
{ 
    return new ReturnMessage(call.MethodBase.Name, new object[0], 0, call.LogicalCallContext, call); 
} 

Purtroppo, tutto quello che vedo nel mio intercettore sono alcune chiamate a GetType, e non il World metodo inesistente .

In caso contrario, qualcuno sa se esiste ancora una versione DynamicProxy che gira felicemente su .NET 4.0 che potrebbe aver risolto il problema?

risposta

17

Inizierò con la risposta lunga. Ogni bind di un funzionamento dinamico in C# fa circa queste tre cose in questo ordine:

  1. Chiedere l'oggetto di legarsi se implementa IDynamicMetaObjectProvider o è un oggetto COM, e se fallisce, allora ...
  2. Associare l'operazione a un'operazione su un oggetto plain-old-clr utilizzando reflection e, in caso contrario, quindi ...
  3. Restituisce un oggetto DynamicMetaObject che rappresenta un errore totale da associare.

Stai vedendo il GetType chiamate perché nella fase 2, il C# di runtime legante sta riflettendo su di voi per cercare di capire se si dispone di un metodo di "Mondo" che è appropriato per chiamare, e questo sta accadendo perché l'implementazione di IDynamicMetaObjectProvider di hello, se ce n'è una, non è riuscita a trovare nulla di speciale da fare.

Sfortunatamente per voi, quando viene generata la RuntimeBinderException, non siamo più vincolanti. L'eccezione viene fuori dalla fase di esecuzione dell'operazione dinamica, in risposta al meta oggetto restituito a causa del passaggio 3. L'unica possibilità per te di prenderlo è nel sito di chiamata vero e proprio.

Così che la strategia non funzionerà per te se vuoi implementare method_missing in C#. Comunque hai alcune opzioni.

Un'opzione facile è quella di implementare IDynamicMetaObjectProvider in MethodMissingInterceptor e rimandare all'implementazione IDMOP dell'oggetto spostato. In caso di errore da parte dell'IDMOP interno, è possibile associare a ciò che si desidera (forse una chiamata a un delegato method_missing memorizzato nell'intercettatore). Il rovescio della medaglia qui è che questo funziona solo per oggetti che sono noti per essere oggetti dinamici, ad es. quelli che implementano IDMOP per cominciare. Questo perché fondamentalmente stai inserendo te stesso tra i passaggi 1 e 2.

Un'altra alternativa che posso pensare è implementare IDynamicMetaObjectProvider e, in esso, rispondere positivamente ad ogni binding, restituendo una chiamata a un metodo che (a) produce lo stesso codice che il compilatore C# avrebbe prodotto per eseguire il binding in primo luogo e (b) rileva RuntimeBinderException per chiamare un metodo method_missing.Il rovescio della medaglia qui è che sarebbe piuttosto complicato - avresti bisogno di generare tipi di delegati arbitrari e l'IL che li usa, contro i tipi pubblici nell'assemblaggio del raccoglitore di runtime C# che, francamente, non sono pensati per il consumo pubblico. Ma almeno avresti il ​​metodo mancante per tutte le operazioni.

Sono sicuro che ci sono altre strategie a cui non ho pensato, come sembra che tu stia accennando all'utilizzo di proxy remoti. Non riesco a immaginare come sono e non posso dire se avranno successo.

Il nocciolo del problema qui è che C# 4.0 non ha un design che anticipa il tuo desiderio di farlo. Nello specifico, non puoi facilmente inserirti tra i passaggi 2 e 3. Questo mi porta alla risposta breve, che è dispiaciuta, C# 4.0 non ha method_missing.

+0

Grazie per l'eccellente spiegazione, Chris: ho appena iniziato a leggere la tua serie di post "dinamici" C# sul tuo blog. :) Per i miei scopi, la tua prima soluzione sembra funzionare correttamente. Voglio davvero fare queste chiamate per gli oggetti in stile builder e per una maggiore fluidità nel test delle API, non ho bisogno di catturarle su oggetti arbitrari. – Thom

+0

@Chris - nota anche la domanda "just in case" sopra; in realtà, mi piacerebbe sapere che ;-p –

+0

C'è un modo per interrogare l'oggetto dinamico solo per la presenza di un membro senza effettivamente chiamarlo? –

Problemi correlati