2010-04-14 12 views
8

Beh, non so se "denominazione forte" è il termine giusto, ma quello che voglio fare è come segue.Posso utilizzare gli strumenti di costruzione di oggetti Ninject con un nome forte?

Attualmente utilizzo ConstructorArgument come ad es. questo:

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")); 
} 

Ora, se io rinominare il parametro "nome" (per esempio usando ReSharper) il ConstructorArgument non aggiornerà, e mi metterò un errore di runtime durante la creazione della Ninja. Per risolvere questo problema ho bisogno di trovare manualmente tutti i posti che specificano questo parametro attraverso un oggetto Constructor e aggiornarlo. Non va bene, e sono destinato a fallire a un certo punto anche se ho una buona copertura di test. Rinominare dovrebbe essere un'operazione a basso costo.

Esiste un modo per specificare un riferimento al parametro, in modo che venga aggiornato quando rinominare il parametro?

risposta

5

Se è possibile condividere più di ciò che si sta veramente cercando di ottenere, si otterrà una risposta migliore. In generale, non si vuole fare affidamento sul passaggio di un ConstructorArgument se ciò può essere aiutato - dovrebbe essere un modo estremo per archiviare un valore di parametro nella creazione di un componente che non si possiede e quindi poter contare su non essere ribattezzato [come] volente o nolente durante le attività di refactoring. Quindi per il codice normale, se si può provare a tenerlo alle interfacce per rendere le cose univoche e non fare affidamento sui nomi, è meglio.

Non riesco a trovare un esempio in questo momento, ma c'è un idioma piuttosto comune chiamato static reflection. Un ConstructorArgument fornito può corrispondere a qualsiasi parametro di quel nome attraverso uno qualsiasi dei costruttori, quindi la riflessione statica non è la cosa più appropriata in questa istanza.

qui la migliore che ti riflessione statica probabilmente permetterà di raggiungere è qualcosa di simile:

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

Il tipico esempio vedrete è dove si vuole estrarre il nome di una proprietà a cui si accede su un caso. Questo è un po 'diverso in quanto ha bisogno di lavorare su un'espressione di richiamo del costruttore.

Come per trovare una lib appropriata che già ha questo, esercitare per il ricercatore: D (Ma io suggerirei di trovare un modo migliore per esprimere ciò che si vuole fare che non usa mai lo ConstructorArgument con questo approccio.)

+0

Lo sto usando solo con componenti che possiedo, quindi non cambieranno improvvisamente a meno che non glielo dica. Ma non voglio avere le mani legate quando refactoring ... – stiank81

+0

Capisco che uno sicuramente non vorrebbe che il tuo codice fosse fragile se tu dovessi citare ConstructorArgument (da qui io + 1ing la domanda). Stavo dicendo che se possiedi il codice probabilmente puoi fare meglio di appoggiare ConstructorArgument in primo luogo [mentre se non hai il codice, ci sono ragioni più legittime per farlo, ma gli impatti negativi sarebbero mitigati una laurea dal fatto che è meno probabile che cambi). –

+0

Non preoccuparti: capisco la domanda e non sto cercando di darti un'altra non risposta che non risolva il problema che potresti aver pensato tu stesso. (Non mi sarei preoccupato di rispondere se non fosse stato per il fatto che sentivo che non avevi ancora una risposta che ascoltava la tua domanda.) –

4

Come già notato, questo approccio è molto fragile e dovrebbe essere evitato. Prova un design nel caso non fosse necessario aggiungere il nome come argomento costruttore. Si potrebbe fare questo ad esempio:

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"; 
} 

Spero che questo aiuti.

+1

Thx per il commento, ma non so .. L'iniezione di proprietà può essere anche fragile. Se il Ninja non può essere usato senza un Nome, devi avere fiducia in tutti per ricordare di aggiungere un Nome dopo aver creato il Ninja. – stiank81

+2

Non necessariamente. Quando non si aggiunge il nome non è valido, la classe 'Ninja' dovrebbe controllarla quando usata (e lanciare un 'InvalidOperationException' altrimenti). Non dimenticare che con la tua soluzione iniziale (nella tua domanda) hai lo stesso problema.Puoi anche creare un metodo di produzione che si occuperà di questo (la fabbrica chiamerà il tuo contenitore). In questo modo, la creazione e l'inizializzazione verranno eseguite solo in un punto. – Steven

+0

Certo - Ho un problema nella mia soluzione iniziale, ma non lo stesso problema. Factory sembra una buona idea. Darò un'occhiata per vedere se può aiutare nella mia situazione! – stiank81

0

Sì, è possibile.

ho imlemented questo:

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(); 
    } 

sua senza LINQ, ma è un buon punto di partenza per capire come il suo lavoro.

Problemi correlati