2010-04-19 4 views
5

Quando si utilizza l'oggetto Constructor di Ninjects è possibile specificare il valore esatto da iniettare su parametri specifici. Perché questo valore non può essere nullo, o come posso farlo funzionare? Forse non è qualcosa che ti piacerebbe fare, ma voglio usare nel mio test di unità .. Esempio:Perché non posso iniettare il valore null con Ninjects ConstructorArgument?

public class Ninja 
{ 
    private readonly IWeapon _weapon; 
    public Ninja(IWeapon weapon) 
    { 
     _weapon = weapon; 
    } 
} 

public void SomeFunction() 
{ 
    var kernel = new StandardKernel(); 
    var ninja = kernel.Get<Ninja>(new ConstructorArgument("weapon", null)); 
} 
+0

Non ho familiarità con ninject, ma penso che il problema sia che il contenitore IoC utilizza le informazioni sul tipo per trovare un costruttore adatto e non può trovarlo per valore nullo. Faresti meglio a cercare la risposta nella documentazione o nel suo codice. In una libreria che ho usato l'istanza dell'oggetto Type corrispondente deve essere passato invece di null. Potrebbe esserci qualche soluzione simile anche qui. – SergGr

+0

Grazie signore :-) – stiank81

risposta

7

Guardando alla fonte (e la traccia dello stack ho ottenuto da reproing cui è omesso: P)

Questo è perché è vincolante per un diverso sovraccarico del ConstructorArgument ctor che l'uso normale (cioè, in cui si' re passando un tipo di valore o un tipo di riferimento non nullo).

La soluzione è quella di lanciare il nulla per oggetto: -

var ninja = kernel.Get<Ninja>(new ConstructorArgument("weapon", (object)null)); 

Ninject 2 fonte:

public class ConstructorArgument : Parameter 
{ 
    /// <summary> 
    /// Initializes a new instance of the <see cref="ConstructorArgument"/> class. 
    /// </summary> 
    /// <param name="name">The name of the argument to override.</param> 
    /// <param name="value">The value to inject into the property.</param> 
    public ConstructorArgument(string name, object value) : base(name, value, false) { } 

    /// <summary> 
    /// Initializes a new instance of the <see cref="ConstructorArgument"/> class. 
    /// </summary> 
    /// <param name="name">The name of the argument to override.</param> 
    /// <param name="valueCallback">The callback to invoke to get the value that should be injected.</param> 
    public ConstructorArgument(string name, Func<IContext, object> valueCallback) : base(name, valueCallback, false) { } 
} 

Repro:

public class ReproAndResolution 
{ 
    public interface IWeapon 
    { 
    } 

    public class Ninja 
    { 
     private readonly IWeapon _weapon; 
     public Ninja(IWeapon weapon) 
     { 
      _weapon = weapon; 
     } 
    } 

    [Fact] 
    public void TestMethod() 
    { 
     var kernel = new StandardKernel(); 
     var ninja = kernel.Get<Ninja>(new ConstructorArgument("weapon", (object)null)); 
    } 
} 

lezione? Saresti pazzo a non scaricare l'ultima fonte e a guardarla. Grandi commenti, bella base di codice pulita. Grazie ancora a @Ian Davis per questo suggerimento!

+0

+1 Bello - roba interessante. – Finglas

+0

Thx per la spiegazione. Non penso di voler andare in giro a trasmettere null agli oggetti, ma questo sembra rispondere alla mia domanda! Forse dovrò controllare l'ultima fonte :-) – stiank81

+0

@ stiank81: Come il mio commento sulla risposta di @Finglas (che credo sia la risposta giusta anche se sono d'accordo che questo dovrebbe essere Accettato: P) dice, la ragione è brutta è perché non è destinato ad essere fatto nell'uso normale (non abbiamo trattato ConstructorArgument come approccio di default errato in un'altra domanda?: P) @Finglas: Grazie, e grazie per rimuovere le altre cose –

0

Questo è probabilmente supportato perché argomenti del costruttore possono essere i tipi di valore troppo.

+0

Questo mi ha spinto a trovare la vera risposta come sembrava sbagliata. Ma mi ha anche spinto a riflettere per non essere d'accordo con la risposta di Brian (lascerei entrambi +0 come troppo tardi per annullare l'altro e mentre entrambi hanno fornito una visione concettuale, non credo che rappresentino qualcosa che Ninject sta cercando di incoraggiare/prevenire/forza) –

3

Non conosco Ninject, ma l'iniezione del costruttore AFAIK è comunemente utilizzata per dipendenze obbligatorie e quindi null ha poco senso in questo contesto. Se la dipendenza non è obbligatoria, il tipo deve fornire un costruttore predefinito e utilizzare invece l'iniezione di proprietà.

This post fornisce ulteriori informazioni.

+0

I + 1d questo perché sembrava più logico della risposta di Gerrie.Riflettendoci, non sono d'accordo sul fatto che possa essere usato sia per i tipi di valore che per i tipi di riferimento e il punto non si generalizza bene in quel contesto. Ciò che si vuole evitare è non specificato, non inizializzato in modo casuale e/o dipendenze che non sono specificati in modo chiaro che gli strumenti possono aiutare a identificare le catene di dipendenza con. –

+0

L'iniezione di proprietà è una soluzione accettabile per questo? Se è così sembra una cosa ragionevole da fare. Grazie per il link – stiank81

+0

@ stiank81: Mentre l'injection di proprietà può realizzare la cosa di base che stai cercando di ottenere (molti valori 'null'), così facendo perderai molto in Code Cleanliness, quindi direi no - I ti rimando alla risposta di @Finglas. –

3

voglio usarlo nella mia unità mette alla prova

C'è no need to use an IOC container for unit tests. È necessario utilizzare il contenitore per collegare l'applicazione insieme in fase di esecuzione e nient'altro. E se questo comincia a far male, è un odore che indica la classe sta sfuggendo di mano

tuo test di unità sarebbe quindi in questo esempio (violazione SRP?):

var ninja = new Ninja(null); 

Quanto sopra è legit codice C# e il passaggio di un riferimento nullo per il test dell'unità è un metodo perfettamente valido per le aree di test delle unità che non richiedono la dipendenza.

+0

+1 Punto importante anche se non risponde la domanda (anche se il fatto che questo sia il caso è probabilmente il motivo per cui questo problema (la risoluzione di un sovraccarico errato in caso di 'null') non è stato considerato un problema. –

+0

@Ruben: Va bene, oltre a ciò che SO è tutto Migliorare e imparare. – Finglas