2012-05-10 16 views
7

Si è verificato un problema di runtime dopo alcuni refactoring e si è bloccato fino alla seguente situazione.Il passaggio di un parametro dinamico genera RuntimeBinderException quando si chiama Method dall'interfaccia ereditata

Quando si passa una proprietà da un oggetto dinamico a un metodo su un'interfaccia ereditata da un'interfaccia padre, il raccoglitore di runtime non è in grado di trovare il metodo.

Ecco un test per dimostrare sia il fallimento e il successo (quando si chiama il metodo direttamente sul tipo di interfaccia madre)

using System.Dynamic; 
using Microsoft.CSharp.RuntimeBinder; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 

namespace Test.Utility 
{ 
    public interface IEcho 
    { 
     string EchoString(string input); 
    } 

    public interface IInheritEcho : IEcho 
    { } 

    public class EchoClass : IInheritEcho 
    { 
     public string EchoString(string input) 
     { 
      return input; 
     } 
    } 

    [TestClass] 
    public class RuntimeBinderTest 
    { 
     [TestMethod] 
     public void RuntimeBinder_should_work_when_dynamic_parameters_are_passed_to_method_from_inherited_interface() 
     { 
      //Arrange 
      dynamic dynObject = new ExpandoObject(); 
      dynObject.Foo = "Bar"; 
      IInheritEcho echomore = new EchoClass(); 

      string echo = null; 
      string exceptionMessage = null; 

      //Act 
      try 
      { 
       echo = echomore.EchoString(dynObject.Foo); 
      } 
      catch (RuntimeBinderException e) 
      { 
       exceptionMessage = e.Message; 
      } 

      //Assert 
      Assert.AreEqual(echo, dynObject.Foo, false, exceptionMessage); 
     } 

     [TestMethod] 
     public void RuntimeBinder_should_work_when_dynamic_parameters_are_passed_to_method_from_noninherited_interface() 
     { 
      //Arrange 
      dynamic dynObject = new ExpandoObject(); 
      dynObject.Foo = "Bar"; 
      IEcho echomore = new EchoClass(); 

      string echo = null; 
      string exceptionMessage = null; 

      //Act 
      try 
      { 
       echo = echomore.EchoString(dynObject.Foo); 
      } 
      catch (RuntimeBinderException e) 
      { 
       exceptionMessage = e.Message; 
      } 

      //Assert 
      Assert.AreEqual(echo, dynObject.Foo, false, exceptionMessage); 
     } 
    } 
} 

Test # 1 non riesce: Assert.AreEqual riuscita. Previsto: < (null)>. Effettivo:. 'Test.Utility.IInheritEcho' non contiene una definizione per 'EchoString'

Test # 2 Riesce.

La mia domanda è se la mia ipotesi che il primo test debba passare è corretta o c'è una ragione fondamentale nel quadro che non lo è?

So che posso risolvere il problema colando i parametri quando li passo o li assegno alle variabili prima di passarle. Sono più curioso sul motivo per cui l'interfaccia ereditata sta causando il fallimento di RuntimeBinder. ..

+0

Hai perfettamente ragione. (Sebbene sia di una versione precedente di C#: http://msdn.microsoft.com/en-us/library/aa664578%28VS.71%29.aspx) Deve esserci un errore. Cosa succede se si utilizza una classe regolare con una proprietà stringa anziché l'oggetto expando? Trova il metodo nell'interfaccia ereditata? (Non avere VS qui per fare il test da solo). Se era con il binding statico (binding in tempo di compilazione), allora c'è qualcosa di sbagliato con il raccoglitore dinamico (run time binder). – JotaBe

+0

L'utilizzo di una classe regolare con una proprietà stringa passa come previsto. È sicuramente il legatore di runtime che non si comporta ... – piff

+0

Se si guarda il link di risposta di igofed a Microsof Connect, questo bug è knwon e molto probabilmente non verrà risolto nella nuova versione di VS. – JotaBe

risposta

2

Una buona domanda.

Sembrerebbe che stia prendendo il tipo dell'espressione in fase di compilazione, IInheritEcho, e non esegua ricerche approfondite sui membri delle interfacce ereditate quando si cerca il metodo da richiamare dinamicamente.

e, idealmente, il legante # runtime C per un dynamic espressione dovrebbe comportarsi allo stesso modo come il compilatore C# - quindi dovrebbe vedere che l'interfaccia IEcho viene ereditato da IInheritEcho e dovrebbe funzionare.

Siamo in grado di verificare l'ipotesi - vale a dire che è la tipizzazione statica - in questo modo nella prima prova:

echo = ((dynamic)echomore).EchoString(dynObject.Foo); 

Hey presto - il test viene superato.

Quindi il problema non è che il legante dinamica non riesce a trovare il metodo - è che quando l'istanza di cui membro viene richiamato in modo dinamico è staticamente tipizzato come interfaccia, interfacce ereditate non vengono consultati.

A mio parere questo comportamento non è corretto.

Si prega di Eric Lippert ... bello ...

4

Voi situazioni è un problema documentato sul Microsoft Connect

+0

Quindi un problema noto. Segue a [questa domanda] (http://stackoverflow.com/questions/3696047/why-calling-isetdynamic-contains-compiles-but-throws-an-exception-at-runtim) che è una buona discussione sul problema – piff

+0

FYI, per chiunque lo trovi di nuovo tramite google, il giudizio finale di Microsoft su questo bug è "chiuso come non risolverà" il 4/6/2011 a causa del suo "ambito". boo. :( – longda

+0

E cinque anni dopo è ancora fuori portata? –

Problemi correlati