Tipos que aceptan valores NULL: mejor manera de verificar nulos o cero en c #

85

Estoy trabajando en un proyecto en el que encuentro que estoy comprobando lo siguiente en muchos, muchos lugares:

if(item.Rate == 0 || item.Rate == null) { }

más como curiosidad que cualquier otra cosa, ¿cuál es la mejor manera de verificar ambos casos?

He agregado un método de ayuda que es:

public static bool nz(object obj)
{
    var parsedInt = 0;
    var parsed = int.TryParse(obj.ToString(), out parsedInt);
    return IsNull(obj) || (parsed && parsedInt == 0);
}

¿Existe una forma mejor?

nailitdown
fuente

Respuestas:

161

me gusta if ((item.Rate ?? 0) == 0) { }

Actualización 1:

También puede definir un método de extensión como:

public static bool IsNullOrValue(this double? value, double valueToCheck)
{
    return (value??valueToCheck) == valueToCheck;
}

Y utilícelo así:

if(item.IsNullOrValue(0)){} // pero no obtienes mucho de eso

eglasius
fuente
3
gracias - muy sucinto! Estaba preocupado por la legibilidad, pero llegué a la conclusión de que sería perfectamente legible si hubiera entendido el ?? operador.
nailitdown
2
Debería utilizar el método de extensión; Si bien es legible en el momento de escribir, estas pequeñas pepitas de código requieren una fracción de pensamiento, lo que significa que si intenta leer el código y lo usa, se distrae de su problema principal.
configurador
11
@nailitdown: no realmente, solo necesitas un método de extensión para Nullable <T>. ¿doble? es un alias para Nullable <double>. Su firma se verá así: public static bool IsNullOrValue <T> (este Nullable <T>, t valueToCheck) donde T: struct
Joshua Shannon
3
@nailitdown: (parte II) Su declaración de devolución se vería entonces como return (value ?? default (T)). Equals (valueToCheck);
Joshua Shannon
2
Si lo acortara a "IsNullOr (0)", naturalmente se leería como "es nulo o cero"
ChrisFox
41

Usando genéricos:

static bool IsNullOrDefault<T>(T value)
{
    return object.Equals(value, default(T));
}

//...
double d = 0;
IsNullOrDefault(d); // true
MyClass c = null;
IsNullOrDefault(c); // true

Si Tes un tipo de referencia , valuese comparará con null( default(T)), de lo contrario, si Tes a value type, digamos doble, default(t)es 0d, para bool es false, para char es '\0'y así sucesivamente ...

Christian C. Salvadó
fuente
1
método de extensión:public static bool IsNullOrValue<T>(this T? value, T valueToCheck) where T : struct { return (value ?? default(T)).Equals(valueToCheck); }
Behzad Ebrahimi
39

Aunque me gusta bastante la respuesta aceptada, creo que, para completar, esta opción también debería mencionarse:

if (item.Rate.GetValueOrDefault() == 0) { }

Esta solución


¹ Sin embargo, esto no debería influir en su decisión, ya que es poco probable que este tipo de microoptimización marque una diferencia.

Heinzi
fuente
19

En realidad, esto es solo una expansión de la respuesta aceptada de Freddy Rios que solo usa genéricos.

public static bool IsNullOrDefault<T>(this Nullable<T> value) where T : struct
{
    return default(T).Equals( value.GetValueOrDefault() );
}

public static bool IsValue<T>(this Nullable<T> value, T valueToCheck) where T : struct
{
    return valueToCheck.Equals((value ?? valueToCheck));
}

NOTA: ¡no es necesario que verifiquemos el valor predeterminado (T) para nulo ya que estamos tratando con tipos de valor o estructuras! Esto también significa que podemos asumir con seguridad que T valueToCheck no será nulo; ¿Recuerdas aquí esa T? es la abreviatura Nullable <T>, por lo que al agregar la extensión a Nullable <T> obtenemos el método en int ?, double ?, bool? etc.

Ejemplos:

double? x = null;
x.IsNullOrDefault(); //true

int? y = 3;
y.IsNullOrDefault(); //false

bool? z = false;
z.IsNullOrDefault(); //true
Joshua Shannon
fuente
2

Estoy de acuerdo con usar el ?? operador.

Si está tratando con cadenas, use if (String.IsNullOrEmpty (myStr))

Nick Josevski
fuente
2

hay una mejor manera?

Bueno, si realmente está buscando una mejor manera, probablemente pueda agregar otra capa de abstracción además de Rate. Bueno, aquí hay algo que se me ocurrió usando Nullable Design Pattern.

usando el sistema;
utilizando System.Collections.Generic;

espacio de nombres NullObjectPatternTest
{
    Programa de clase pública
    {
        public static void Main (cadena [] argumentos)
        {
            var items = nueva lista
                            {
                                nuevo artículo (RateFactory.Create (20)),
                                nuevo elemento (RateFactory.Create (nulo))
                            };

            PrintPricesForItems (artículos);
        }

        PrintPricesForItems vacío estático privado (elementos IEnumerable)
        {
            foreach (var elemento en elementos)
                Console.WriteLine ("Precio del artículo: {0: C}", item.GetPrice ());
        }
    }

    ItemBase de clase abstracta pública
    {
        tarifa pública abstracta Tarifa {get; }
        public int GetPrice ()
        {
            // NO es necesario comprobar si Rate == 0 o Rate == null
            return 1 * Rate.Value;
        }
    }

    Artículo de clase pública: ItemBase
    {
        privado readonly Rate _Rate;
        Tasa de tasa de anulación pública {get {return _Rate; }}
        Artículo público (tarifa de tarifa) {_Rate = tarifa; }
    }

    RateFactory de clase pública sellada
    {
        Crear tasa estática pública (int? rateValue)
        {
            if (! rateValue || rateValue == 0) 
                return new NullRate ();
            return new Rate (rateValue);
        }
    }

    Tarifa de clase pública
    {
        public int Value {obtener; conjunto; }
        public virtual bool HasValue {get {return (Valor> 0); }}
        Public Rate (int valor) {Valor = valor; }
    }

    clase pública NullRate: Rate
    {
        public override bool HasValue {get {return false; }}
        public NullRate (): base (0) {}
    }
}
dance2die
fuente
1
Creo que tiene razón en que eliminar los valores nulos en una etapa anterior habría sido el camino a seguir
nailitdown
No exactamente. Existe un concepto llamado "Refactorización". Puede refactorizar su código hacia un mejor patrón o una mejor estructura. Siempre puede eliminar los valores que aceptan valores NULL en etapas posteriores.
dance2die
2

Su muestra de código fallará. Si obj es nulo, obj.ToString () dará como resultado una excepción de referencia nula. Cortaría el proceso y verificaría un obj nulo al comienzo de su función de ayuda. En cuanto a su pregunta real, ¿cuál es el tipo que está buscando nulo o cero? En String hay una gran función IsNullOrEmpty, me parece que este sería un gran uso de los métodos de extensión para implementar un método IsNullOrZero en el int. tipo.

Editar: Recuerde, el '?' es solo el azúcar del compilador para el tipo INullable, por lo que probablemente podría tomar un INullable como parm y luego compararlo con null (parm == null) y, si no es null, compararlo con cero.

Walden Leverich
fuente
saludos por detectar ese error: en este proyecto, necesito verificar con int ?, ¿doble ?, decimal ?, etc., razón por la cual estoy usando "object"
nailitdown
Recuerda el '?' es solo azúcar de compilación para el tipo INullable <T>, por lo que probablemente podría tomar un INullable <T> como parm y luego compararlo con null (parm == null) y, si no, compararlo con cero.
Walden Leverich
0
public static bool nz(object obj)
{
    return obj == null || obj.Equals(Activator.CreateInstance(obj.GetType()));
}

fuente
1
La reflexión es lenta , ten cuidado con soluciones como esta. No me opongo a la reflexión, pero no lo esperaría en una función "simple" como esta y me tomaría desprevenido.
Walden Leverich
¿Esto realmente comprueba cero?
nailitdown
En realidad no. Activator.CreateInstance (obj.GetType ()) devolverá nulo para los tipos que aceptan valores NULL, creo.
configurador
@configurator: cuando los tipos que aceptan valores NULL están encuadrados, están encuadrados como la estructura subyacente, es decir, por lo que esto no construirá una estructura anulable de valores nulos, sino una estructura no anulable con valores predeterminados.
Eamon Nerbonne
0
class Item{  
 bool IsNullOrZero{ get{return ((this.Rate ?? 0) == 0);}}
}
destruir
fuente
su solución funciona para una propiedad, debería haber especificado que tengo muchas propiedades del mismo elemento para verificar si hay nulo / cero
nailitdown
0

No olvide, para las cadenas, siempre puede usar:

String.IsNullOrEmpty(str)

En vez de:

str==null || str==""
Chris
fuente
1
La pregunta se compara con "0", no con una cadena vacía.
dance2die
-1

Un paso más allá de la agradable respuesta de Joshua Shannon . Ahora, con la prevención del boxeo / unboxing :

public static class NullableEx
{
    public static bool IsNullOrDefault<T>(this T? value)
        where T : struct
    {
        return EqualityComparer<T>.Default.Equals(value.GetValueOrDefault(), default(T));
    }
}
Deilan
fuente