2011-04-15 13 views
11

Tengo el siguiente código que crea un objeto dinámico que está asignado a la variable smtpClient.¿Por qué una Microsoft.CSharp.RuntimeBinder.RuntimeBinderException si el método invocado está allí?

public class TranferManager 
{ 
    public void Tranfer(Account from, Account to, Money amount) 
    { 
     // Perform the required actions 
     var smtpClient = New.SmtpClient(); 
     smtpClient.Send("[email protected]", "from.Email", "Tranfer", "?"); 
     // In the previous line I get a Microsoft.CSharp.RuntimeBinder.RuntimeBinderException 
     // with the description = "'object' does not contain a definition for 'Send'" 
    } 
} 

public static class New 
{ 
    public static dynamic SmtpClient(params object[] parameters) 
    { 
     return typeof(SmtpClient).New(parameters); 
    } 
} 

public static class CreationExtensions 
{ 
    private static Dictionary<Type, Func<object, dynamic>> builders = 
     new Dictionary<Type, Func<object, dynamic>>(); 

    public static dynamic New(this Type type, params object[] parameters) 
    { 
     if(builders.ContainsKey(type)) 
      return builders[type](parameters); 

     return Activator.CreateInstance(type, parameters); 
    } 

    public static void RegisterBuilder(this Type type, Func<object, dynamic> builder) 
    { 
     builders.Add(type, builder); 
    } 
} 

Para probar que estoy usando el UT (abajo):

[TestMethod()] 
    public void TranferTest() 
    { 
     typeof(SmtpClient).RegisterBuilder(p => 
      new 
      { 
       Send = new Action<string, string, string, string>(
       (from, to, subject, body) => { }) 
      } 
     ); 

     var tm = new TranferManager(); 
     tm.Tranfer(new Account(), new Account(), new Money()); 
     // Assert 
    } 

Cuando, utilizando las ventanas inmediatas, pido para el tipo SMTPClient me sale:

smtpClient.GetType() 
{<>f__AnonymousType0`1[System.Action`4[System.String,System.String,System.String,System.String]]} 

Y cuando pregunto por sus miembros me aparece:

smtpClient.GetType().GetMembers() 
{System.Reflection.MemberInfo[7]} 
    [0]: {System.Action`4[System.String,System.String,System.String,System.String] get_Send()} 
    [1]: {System.String ToString()} 
    [2]: {Boolean Equals(System.Object)} 
    [3]: {Int32 GetHashCode()} 
    [4]: {System.Type GetType()} 
    [5]: {Void .ctor(System.Action`4[System.String,System.String,System.String,System.String])} 
    [6]: {System.Action`4[System.String,System.String,System.String,System.String] Send} 

Entonces, mi pregunta es: ¿por qué recibo esa excepción?

+0

uhmm ... copiar todo el código en una Aplicación de consola (y el contenido de su método de prueba en mi método "principal") no arroja ninguna excepción para mí. Supongo que tu TestMethod está en una dll diferente. Tal vez haya un problema con la versión de .NET a la que se dirige en el dll o sus referencias. –

+0

Lo mismo aquí. Cuando ejecuto su código (habiendo proporcionado definiciones adecuadas de Cuenta, Dinero, etc.) no obtengo ninguna excepción. ¿Puede proporcionar un pequeño programa que * realmente compila, ejecuta y demuestra el problema? * –

+0

Tiene razón, copiando el código de prueba en un método "Principal()" de una aplicación de consola que funciona. Ahora puedo ver el problema: el tipo anónimo (creado en el dll de prueba) no es visible desde su dll. – lontivero

Respuesta

30

Los tipos anónimos son internos, si cruza los límites de ensamblaje dynamic no se puede resolver la propiedad.

En lugar de utilizar un tipo anónimo, intente utilizar un tipo real o un objeto Expando.

+0

Gracias, la razón es correcta y punto clave, ¿puedes dar cualquier solvencia? –

+2

Cambiar las partes internas para que sean visibles para otro ensamblaje también es una buena solución para las pruebas unitarias - http://cstruter.com/blog/278 –

+0

Vote por esta característica en Visual Studio [UserVoice] (https: //visualstudio.uservoice .com/forums/121579-visual-studio/suggestions/7062098-support-object-initializers-on-expandoobjects). – orad

7

En los AssemblyInfo.cs intenta agregar siguiente:

[assembly: InternalsVisibleTo("NameSpace1.SubNameSpace1")] 

donde NamsSpace1 es el nombre del proyecto y el espacio de nombres de subespaciodenombres es su objeto dinámico/anónimo

Cuestiones relacionadas