2009-07-24 20 views
11

Refactoring de Martin Fowler discute la creación de objetos nulos para evitar un montón de¿Cómo se crea un objeto nulo en C#

if (myObject == null) 

pruebas. ¿Cuál es la forma correcta de hacer esto? Mi intento infringe la regla de "llamada de miembro virtual en constructor". Aquí está mi intento de que:

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

¿Cuál es el problema que está tratando de resolver? ¿Qué pasa con las referencias nulas, exactamente? – spoulson

+2

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

+0

Su propiedad IsNull en la clase NullAnimal debe ser una anulación, no una virtual. – TGnat

Respuesta

11

Tiendo a estar de acuerdo con Wyatt Barnett's answer en que debe mostrar restricción al crear este tipo de objetos "nulos". Dicho esto, hay algunas buenas razones para hacerlo. En ocasiones.

También tiendo a estar de acuerdo con Supertux's answer en que el objetivo de un objeto nulo es no tener que comprobar si es o no nulo, por lo que debe perder la propiedad IsNull. Si realmente siente que necesita la propiedad IsNull, lea la respuesta de Wyatt nuevamente y reconsidere.

Y gracias CraigTP for the nice links para más información. Buen material.

Ahora supondré que en su código real usted realmente tiene un constructor que está tratando de establecer los valores de Nombre o Especie (cualquiera que sea el equivalente de su código real). De lo contrario, ¿por qué recibiría la advertencia/error de "llamada de miembro virtual en el constructor"? Me he encontrado con un par de problemas similares al usar MyProperty {get; conjunto; } atajo yo mismo (particularmente cuando se usa en estructuras, y no me explico sobre el control de versiones de serialización). Su solución es no usar el atajo, sino hacerlo a la antigua.

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 { } 
    } 
} 

Esto resuelve el problema de establecer sus propiedades virtuales en el constructor. En su lugar, está configurando los valores de campo privados (algo que no tiene la capacidad de referencia si usa el acceso directo). Para obtener crédito adicional, compile ambos métodos y use el reflector para ver los ensamblados resultantes.

Cuanto más uso el {get; conjunto; } atajo, más me disgusta.

22

ir a buscar a la cantidad de dolor que los conceptos interesantes, como DBNull, han causado y pensar si esto es realmente una buena idea.

Protip: si constantemente está buscando referencias nulas, probablemente debería reconsiderar la API un poco para ayudar a evitar que los objetos nulos estén más cerca de la parte superior de la pila.

Protip II: tener algo arrojar una excepción cuando hay un nulo inesperado es en realidad bien y excelente. Las cosas deberían explotar si tiene nulos donde no debería haber nulo.

+7

+1. El patrón nulo es una IMO anti-patrón. Lanza nulos no deseados y busca nulos cuando estén permitidos. – Randolpho

+1

La idea del patrón NullObject es que las cosas no se disparan, especialmente en producton. – Supertux

+0

@ Wyatt: http://jeremyjarrell.com/archive/2007/08/01/46.aspx tiene una buena demostración de los beneficios de los objetos nulos ... pero no soy un gran admirador de ellos, personalmente. – Brian

2

Consulte estos enlaces, tanto para el patrón de diseño propio (Null Object patrón de diseño) y una aplicación (en C#):

el patrón de diseño:

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

Una implementación:

The Null Object Pattern

+0

Tu último enlace parece estar muerto. – LarsTech

+0

@LarsTech He actualizado el último enlace para apuntar a la [caché de Wayback de Internet Archive] (http://archive.org/web/) caché de la página. – CraigTP

+0

Por cierto, yo no era el voto hacia abajo. – LarsTech

3

El objetivo del patrón Objeto nulo es que no requiere una comprobación nula para evitar un bloqueo o un error.

Por ejemplo, si intentó realizar una operación en la propiedad Especies y fue nula, se produciría un error.

Por lo tanto, no debería ser necesario un método isNull, devuelve otra en el captador que no cause la aplicación se bloquee/error, por ejemplo:

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

Bien, pero esto aún infringe la llamada de miembro virtual en la regla de constructor. – Sisiutl

+0

Técnicamente no, porque no hay constructor. –

2

Sólo se utiliza este enfoque si es apropiado . Su ejemplo de un objeto Animal podría no ser un buen ejemplo porque no presenta un caso apropiado en el que usaría este enfoque. Por ejemplo:

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

En este ejemplo, se debe construir el objeto de animales de modo que si el animal no tiene una cola, que puede manejar correctamente el comando WAGTAIL() sin que se caiga.

Class Animal 
{ 
    Tail tail; 

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

Ahora usted no tiene que hacer un cheque nulo, pero sólo se puede llamar animal.wagTail(), independientemente de si el animal tiene una cola o no.

+0

irregardless/facepalm –

+1

Estoy en línea y me veo como un idiota. ¿Qué más hay de nuevo? – tyriker

+0

Se lo solucionó por usted. –

0

Me gustaría mencionar aquí algunos detalles interesantes. Mira tu clase. ¿Tiene alguna lógica? Esta no es una clase en su sentido, esta es una estructura de datos. Lo que intenta hacer es aplicar un patrón de objeto nulo a algo que no es aplicable. Las estructuras de datos están más cerca de los tipos de valores que de las clases. Por lo tanto, la verificación nula puede estar en su lugar para resolver su problema. El patrón de objeto nulo no es algo que siempre deba seguir. El patrón de objeto nulo es una cosa que puedes usar para evitar la violación del principio de sustitución de Liskov, para representar una clase que no hace nada, porque nulo no es una sustitución apropiada para una clase, ya que es un valor, pero no una clase. Pero las cosas son diferentes con los tipos de valores y las estructuras de datos. ¡Nulo es valioso! Entonces, en este caso, la comprobación nula es lo correcto.

Cuestiones relacionadas