2009-07-24 17 views
11

Refactoring di Martin Fowler discute la creazione di oggetti Null per evitare un sacco diCome si crea un oggetto nullo in C#

if (myObject == null) 

test. Qual è il modo giusto per farlo? Il mio tentativo viola la regola "chiamata membro virtuale nel costruttore". Ecco il mio tentativo di esso:

public class Animal 
{ 
    public virtual string Name { get; set; } 
    public virtual string Species { get; set; } 
    public virtual bool IsNull 
    { 
     get { return false; } 
    } 
} 

public sealed class NullAnimal : Animal 
{ 
    public override string Name 
    { 
     get{ return "NULL"; } 
     set { } 
    } 
    public override string Species 
    { 
     get { return "NULL"; } 
     set { } 
    } 
    public virtual bool IsNull 
    { 
     get { return true; } 
    } 
} 
+3

Qual è il problema che stai cercando di risolvere? Cosa c'è di sbagliato con i riferimenti null, esattamente? – spoulson

+2

http://www.refactoring.com/catalog/introduceNullObject.html – Sisiutl

+0

La proprietà IsNull nella classe NullAnimal deve essere di tipo override, non virtuale. – TGnat

risposta

11

tendo a concordare con Wyatt Barnett's answer in che si dovrebbe dar prova di moderazione durante la creazione di questo tipo di oggetti "nulli". Detto questo, ci sono alcune buone ragioni per farlo. All'occasione.

Tendo anche a concordare con Supertux's answer in quanto il punto intero di un oggetto nullo non è necessario verificare se è nullo, quindi è necessario perdere la proprietà IsNull. Se ritieni davvero di aver bisogno della proprietà IsNull, allora leggi di nuovo la risposta di Wyatt e riconsidera.

E grazie CraigTP for the nice links per ulteriori informazioni. Roba buona.

Ora assumerò che nel proprio codice reale ci sia effettivamente un costruttore che sta tentando di impostare i valori di Nome o Specie (qualunque sia il nome del tuo vero codice equivalente). Altrimenti, perché dovresti ricevere l'avviso/errore "Chiamata virtuale nel costruttore"? Ho riscontrato un paio di problemi simili durante l'utilizzo di MyFrontty newfangled {get; impostato; } scorciatoia me stesso (in particolare se usato nelle strutture e non mi fa iniziare sulla versione della serializzazione). La soluzione è non utilizzare la scorciatoia, ma farlo alla vecchia maniera.

public class Animal { 
    protected Animal() { } 

    public Animal(string name, string species) { 
     _Name = name; 
     _Species = species; 
    } 

    public virtual string Name { 
     get { return _Name; } 
     set { _Name = value; } 
    } 
    private string _Name; 

    public virtual string Species { 
     get { return _Species; } 
     set { _Species = value; } 
    } 
    private string _Species; 
} 

public sealed class NullAnimal : Animal { 
    public override string Name { 
     get { return String.Empty; } 
     set { } 
    } 
    public override string Species { 
     get { return String.Empty; } 
     set { } 
    } 
} 

Questo risolve il problema di impostare le proprietà virtuali nel costruttore. Invece, si impostano i valori dei campi privati ​​(qualcosa che non si ha la possibilità di fare riferimento se si utilizza il collegamento). Per ulteriore credito, compilare entrambi i metodi e utilizzare Reflector per esaminare gli assembly risultanti.

Più uso il {get; impostato; scorciatoia, più mi piace.

22

Andare a cercare la quantità di dolore che i concetti interessanti, come DBNull, hanno causato e pensare se questo è in realtà una buona idea.

Protip: se si verificano costantemente riferimenti null, è probabile che si debba riconsiderare un po 'l'API per impedire che oggetti nulli si avvicinino alla cima dello stack.

Protip II: avere qualcosa di un'eccezione quando v'è un nulla inaspettato è in realtà bene e dandy. Le cose dovrebbero esplodere se hai dei null dove non dovrebbe esserci nulla.

+7

+1. Il modello Null è un IMO anti-pattern. Getta per i null indesiderati e controlla i null quando sono permessi. – Randolpho

+1

L'idea del pattern NullObject è che le cose non vanno a gonfie vele, specialmente in produzione. – Supertux

+0

@Wyatt: http://jeremyjarrell.com/archive/2007/08/01/46.aspx ha una buona dimostrazione dei vantaggi degli oggetti null ... ma io non sono un grande fan di loro, personalmente. – Brian

2

prega di consultare questi collegamenti sia per il modello di progettazione in sé (Null Object Design Pattern) e l'implementazione (in C#):

il modello di progettazione:

Null Object pattern - Wikipedia
Null Object Design Pattern
Introduce Null Object (contains some implementation code)

Un'implementazione:

The Null Object Pattern

+0

Il tuo ultimo link sembra morto. – LarsTech

+0

@LarsTech Ho aggiornato l'ultimo collegamento per puntare invece alla cache della pagina [Internet Archive's Wayback Machine] (http://archive.org/web/). – CraigTP

+0

BTW, non ero il voto negativo. – LarsTech

3

Il punto del modello Null Object è che non richiede un controllo null per impedire un incidente o errore.

Per esempio, se si è tentato di eseguire un'operazione sulla proprietà delle specie ed è stato nulla - avrebbe causato un errore.

Quindi, non dovrebbe essere necessario un metodo isNull, basta tornare qualcosa nel getter tale da non causare l'applicazione in crash/errori es:

public class Animal 
{ 
    public virtual string Name { get; set; } 
    public virtual string Species { get; set; } 
} 

public sealed class NullAnimal : Animal 
{ 
    public override string Name 
    { 
     get{ return string.Empty; } 
     set { ; } 
    } 
    public override string Species 
    { 
     get { return string.Empty; } 
     set { ; } 
    } 
} 
+0

Bene, ma questo continua a violare la chiamata del membro virtuale nella regola del costruttore. – Sisiutl

+0

Tecnicamente no, perché non esiste un costruttore. –

2

Si utilizza solo questo approccio se è appropriato . Il tuo esempio di un oggetto Animal potrebbe non essere un buon esempio perché non presenta un caso appropriato in cui dovresti usare questo approccio. Per esempio:

Animal animal = new Animal(); 

if (animal.tail == null) 
{ 
    //do nothing because wagging a tail that doesn't exist may crash the program 
} 
else 
{ 
    animal.wagTail(); 
} 

In questo esempio, si dovrebbe costruire l'oggetto degli animali in modo che se l'animale non ha una coda, è in grado di gestire correttamente il comando ballerina(), senza schiantarsi.

Class Animal 
{ 
    Tail tail; 

    void wagTail() 
    { 
     if (this.tail == null) 
     { 
      //do nothing 
     } 
     else 
     { 
      this.tail.doTheWag(); 
     } 
    } 
} 

Ora non c'è bisogno di fare un controllo nullo, ma può semplicemente chiamare animal.wagTail() indipendentemente dal fatto che l'animale ha una coda o no.

+0

irregardless/facepalm –

+1

Sono online e sembro un idiota. Cos'altro è nuovo? – tyriker

+0

risolto per voi. –

0

Vorrei menzionare qui alcuni dettagli interessanti. Guarda la tua classe. Ha qualche logica in esso? Questa non è una classe nel suo senso, questa è una struttura dati. Quello che stai cercando di fare è applicare il modello di oggetto nullo a qualcosa a cui non è applicabile. Le strutture dati sono più vicine ai tipi di valore che alle classi. Pertanto, il controllo nullo può essere corretto per risolvere il problema. Il modello di oggetto nullo non è qualcosa che dovresti sempre seguire. Il modello di oggetto nullo è una cosa che puoi usare per evitare la violazione del principio di sostituzione di Liskov, per rappresentare una classe che non fa nulla, perché null non è la sostituzione appropriata per una classe in quanto è un valore, ma non una classe. Ma le cose sono diverse con tipi di valore e strutture dati. Null è un valore! Quindi in questo caso il controllo nullo è la cosa giusta da fare.

Problemi correlati