2010-04-14 6 views
8

Bueno, no sé si el "término correcto" es el término correcto, pero lo que quiero hacer es lo siguiente.¿Puedo usar Ninject ConstructorArguments con nombres fuertes?

Actualmente utilizo ConstructorArgument como p. Ej. esto:

public class Ninja 
{ 
    private readonly IWeapon _weapon; 
    private readonly string _name; 

    public Ninja(string name, IWeapon weapon) 
    { 
     _weapon = weapon; 
     _name = name; 
    } 
    // ..more code.. 
} 

public void SomeFunction() 
{ 
    var kernel = new StandardKernel(); 
    kernel.Bind<IWeapon>().To<Sword>(); 
    var ninja = kernel.Get<Ninja>(new ConstructorArgument("name", "Lee")); 
} 

Ahora, si puedo cambiar el nombre el "nombre" de parámetros (por ejemplo, utilizando ReSharper) la ConstructorArgument no se actualizará, y voy a tener un error de ejecución al crear el Ninja. Para solucionar esto, necesito encontrar todos los lugares manualmente. Especifico este parámetro a través de un ConstructorArgument y lo actualizo. No es bueno, y estoy condenado al fracaso en algún momento a pesar de que tengo una buena cobertura de prueba. Cambiar el nombre debe ser una operación barata.

¿Hay alguna manera en que pueda hacer una referencia al parámetro en su lugar, de modo que se actualice cuando cambie el nombre del parámetro?

Respuesta

5

Si puede compartir más de lo que realmente está tratando de lograr, obtendrá una mejor respuesta. En general, no desea confiar en pasar un ConstructorArgument en absoluto si eso puede ser de ayuda; debería ser una forma de último recurso de calzar el valor de un parámetro en la creación de un componente que no le pertenece y por lo tanto puede confiar en siendo renombrado [como] willy nilly durante las actividades de refactorización. Entonces, para el código normal, si puede tratar de mantenerlo en las interfaces para que las cosas no sean ambiguas y no dependan de los nombres, eso es mejor.

No se puede desenterrar un ejemplo en este momento, pero hay un modismo bastante común llamado static reflection. Un ConstructorArgument suministrado puede coincidir con cualquier parámetro de ese nombre en cualquiera de los constructores, por lo que la reflexión estática no es lo más apropiado en esta instancia.

Por lo tanto la mejor reflexión que va estática es probable que pueda lograr es algo así como:

var ninja = ninject.Get<Ninja>(ParamNamesOf(()=>new Ninja("dummy", "dummy")).First()); 

El ejemplo típico verá es donde se quiere extraer el nombre de una propiedad que se tiene acceso en una instancia. Esto es un poco diferente ya que necesita trabajar en una expresión de invocación de constructor.

En cuanto a encontrar una lib apropiada que ya tiene eso, haga ejercicio para el buscador: D (Pero sugiero encontrar una mejor manera de expresar lo que quiere hacer que no use ConstructorArgument de preferencia a este enfoque de todos modos.)

+0

Estoy usando esto solo con los componentes que tengo, por lo que no cambiarán repentinamente a menos que se los indique. Pero no quiero tener mis manos atadas cuando refacto ... – stiank81

+0

Entiendo que definitivamente no querría que su código fuera frágil si tuviera que demandar a ConstructorArgument (de ahí que yo + 1en la pregunta). Estaba enfatizando que si posees el código probablemente puedas hacer algo mejor que apoyarte en ConstructorArgument en primer lugar [mientras que si no posees el código hay razones más legítimas para hacerlo, pero los impactos negativos se mitigarían a un grado por el hecho de que es menos probable que cambie]. –

+0

No se preocupe: entiendo la pregunta y no estoy tratando de darle otra falta de respuesta que no resuelva el problema que podría haber pensado usted mismo. (No me hubiera molestado en responder si no fuera por el hecho de que sentí que aún no tienes una respuesta que haya escuchado tu pregunta). –

4

Como ya ha notado, este enfoque es muy frágil y debe evitarse. Pruebe un diseño si no necesita agregar el nombre como argumento de constructor. Podría hacer esto, por ejemplo:

public class Ninja 
{ 
    private readonly IWeapon _weapon; 

    public Ninja(IWeapon weapon) 
    { 
     _weapon = weapon; 
    } 

    public string Name { get; set; } 

    // ..more code.. 
} 

public void SomeFunction() 
{ 
    var kernel = new StandardKernel(); 
    kernel.Bind<IWeapon>().To<Sword>(); 
    var ninja = ninject.Get<Ninja>(); 
    ninja.Name = "Lee"; 
} 

Espero que esto ayude.

+1

Thx para el comentario, pero no sé ... La inyección de propiedad también puede ser frágil. Si el Ninja no puede usarse sin un Nombre, debes confiar en que todos recuerden haber agregado un Nombre después de crear el Ninja. – stiank81

+2

No necesariamente. Cuando no se agrega el nombre no es válido, la clase 'Ninja' debe verificar esto cuando se usa (y arrojar una' InvalidOperationException' de lo contrario). No olvide que con su solución inicial (en su pregunta) tiene el mismo problema.También puede crear un método de fábrica que se encargue de esto (la fábrica llamará a su contenedor). De esta forma, la creación y la inicialización solo se realizarán en un solo lugar. – Steven

+0

Sure - Tengo un problema en mi solución inicial, pero no el mismo problema. Factory suena como una buena idea. ¡Echaré un vistazo para ver si puede ayudar en mi situación! – stiank81

0

Sí, puedes.

He imlemented esto:

public string GiveConstuctorArgumentName(Type class, Type constructorArgument) 
    { 
     var cons = class.GetConstructors(); 

     foreach (var constructorInfo in cons) 
     { 
      foreach (var consParameter in constructorInfo.GetParameters()) 
      { 
      if (consParameter.ParameterType == constructorArgument) 
      { 
       return consParameter.Name; 
      } 
      } 
     } 

     throw new InstanceNotFoundException(); 
    } 

Su sin LINQ, pero es un buen punto de partida para comprender cómo su trabajo.

Cuestiones relacionadas