Tipi nullable: modo migliore per verificare null o zero in c #


85

Sto lavorando a un progetto in cui trovo che sto verificando quanto segue in molti, molti posti:

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

più come curiosità che altro, qual è il modo migliore per verificare entrambi i casi?

Ho aggiunto un metodo di supporto che è:

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

Esiste un modo migliore?

Risposte:


162

mi piace if ((item.Rate ?? 0) == 0) { }

Aggiornamento 1:

Puoi anche definire un metodo di estensione come:

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

E usalo in questo modo:

if(item.IsNullOrValue(0)){} // ma non ci guadagni molto


3
grazie - molto succinto! Ero preoccupato per la leggibilità, ma sono giunto alla conclusione che sarebbe stato perfettamente leggibile se avessi effettivamente capito ?? operatore.
nailitdown

2
Dovresti usare il metodo di estensione; sebbene sia leggibile al momento della scrittura, questo piccolo frammento di codice richiede una frazione di pensiero, il che significa che se provi a leggere il codice e lo usa, sei distratto dal tuo problema principale.
configuratore

11
@nailitdown: non proprio, hai solo bisogno di un metodo di estensione per Nullable <T>. Doppio? è un alias per Nullable <double>. La tua firma sarà simile a: public static bool IsNullOrValue <T> (this Nullable <T>, t valueToCheck) dove T: struct
Joshua Shannon

3
@nailitdown: (parte II) La tua dichiarazione di ritorno sarebbe quindi come return (value ?? default (T)). Equals (valueToCheck);
Joshua Shannon

2
Se abbreviato in "IsNullOr (0)", verrebbe naturalmente letto come "è nullo o zero"
ChrisFox

41

Usare i generici:

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

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

Se Tè un tipo di riferimento , valueverrà confrontato con null( default(T)), altrimenti, se Tè value type, diciamo double, default(t)è 0d, per bool is false, per char is '\0'e così via ...


1
metodo di estensione:public static bool IsNullOrValue<T>(this T? value, T valueToCheck) where T : struct { return (value ?? default(T)).Equals(valueToCheck); }
Behzad Ebrahimi

39

Sebbene mi piaccia abbastanza la risposta accettata, penso che, per completezza, dovrebbe essere menzionata anche questa opzione:

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

Questa soluzione


¹ Ciò non dovrebbe influenzare la tua decisione, tuttavia, poiché è improbabile che questo tipo di microottimizzazione faccia la differenza.


19

Questa è davvero solo un'espansione della risposta accettata di Freddy Rios usando solo i Generics.

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 non abbiamo bisogno di controllare default (T) per null poiché abbiamo a che fare con tipi di valore o strutture! Ciò significa anche che possiamo tranquillamente presumere che T valueToCheck non sarà nullo; Ricordi qui quella T? è una scorciatoia Nullable <T> quindi aggiungendo l'estensione a Nullable <T> otteniamo il metodo in int ?, double ?, bool? eccetera.

Esempi:

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

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

bool? z = false;
z.IsNullOrDefault(); //true

2

Sono d'accordo con l'utilizzo del ?? operatore.

Se hai a che fare con stringhe usa if (String.IsNullOrEmpty (myStr))


2

Esiste un modo migliore?

Bene, se stai davvero cercando un modo migliore, probabilmente puoi aggiungere un altro livello di astrazione oltre a Rate. Bene, ecco qualcosa che ho appena inventato usando Nullable Design Pattern.

using System;
using System.Collections.Generic;

spazio dei nomi NullObjectPatternTest
{
    programma di classe pubblica
    {
        public static void Main (string [] args)
        {
            elementi var = nuovo elenco
                            {
                                nuovo elemento (RateFactory.Create (20)),
                                nuovo elemento (RateFactory.Create (null))
                            };

            PrintPricesForItems (articoli);
        }

        private static void PrintPricesForItems (IEnumerable items)
        {
            foreach (var item in items)
                Console.WriteLine ("Prezzo articolo: {0: C}", item.GetPrice ());
        }
    }

    classe astratta pubblica ItemBase
    {
        abstract pubblico Rate Rate {get; }
        public int GetPrice ()
        {
            // NON è necessario verificare se Rate == 0 o Rate == null
            return 1 * Rate.Value;
        }
    }

    classe pubblica Item: ItemBase
    {
        privato readonly Rate _Rate;
        public override Rate Rate {get {return _Rate; }}
        Articolo pubblico (Tasso tasso) {_Rate = tasso; }
    }

    classe pubblica sigillata RateFactory
    {
        tasso statico pubblico Crea (int? rateValue)
        {
            if (! rateValue || rateValue == 0) 
                return new NullRate ();
            return new Rate (rateValue);
        }
    }

    tasso di classe pubblica
    {
        public int Valore {get; impostato; }
        bool virtuale pubblico HasValue {get {return (Value> 0); }}
        Tasso pubblico (valore int) {Valore = valore; }
    }

    public class NullRate: Rate
    {
        public override bool HasValue {get {return false; }}
        public NullRate (): base (0) {}
    }
}

1
Penso che tu abbia ragione sul fatto che eliminare i valori nulli in una fase precedente sarebbe stata la strada da percorrere
nailitdown

Non esattamente. C'è un concetto chiamato "Refactoring". Puoi effettuare il refactoring del codice verso un modello migliore o una struttura migliore. È sempre possibile eliminare i valori nullable nelle fasi successive.
dance2die

2

L'esempio di codice fallirà. Se obj è null, obj.ToString () risulterà in un'eccezione di riferimento null. Abbreviare il processo e verificare la presenza di un oggetto nullo all'inizio della funzione di supporto. Per quanto riguarda la tua domanda effettiva, qual è il tipo che stai verificando per null o zero? Su String c'è una grande funzione IsNullOrEmpty, mi sembra che questo sarebbe un ottimo uso dei metodi di estensione per implementare un metodo IsNullOrZero sull'int? genere.

Modifica: ricorda, il "?" è solo zucchero del compilatore per il tipo INullable, quindi potresti probabilmente prendere un INullable come parametro e quindi confrontarlo con null (parm == null) e se non null confrontare con zero.


applausi per aver catturato quel bug - in questo progetto
devo

Ricorda il '?' è solo zucchero del compilatore per il tipo INullable <T>, quindi potresti probabilmente prendere un INullable <T> come parametro e quindi confrontarlo con null (parm == null) e se non null confrontare con zero.
Walden Leverich

0
public static bool nz(object obj)
{
    return obj == null || obj.Equals(Activator.CreateInstance(obj.GetType()));
}

1
La riflessione è lenta , fai attenzione a soluzioni come questa. Non mi oppongo alla riflessione, ma non me lo aspetterei in una funzione "semplice" come questa e mi prenderebbe alla sprovvista.
Walden Leverich

questo verifica effettivamente zero?
nailitdown

In realtà no. Activator.CreateInstance (obj.GetType ()) restituirà null per i tipi nullable, credo.
configuratore

@configurator: quando i tipi nullable sono boxed, sono boxed come la struttura sottostante, cioè, quindi questo non costruirà una struttura nullable con valori null, ma una struttura non nullable con valori predefiniti.
Eamon Nerbonne

0
class Item{  
 bool IsNullOrZero{ get{return ((this.Rate ?? 0) == 0);}}
}

la tua soluzione funziona per una proprietà, avrei dovuto specificare che ho molte proprietà dello stesso articolo per verificare null / zero
nailitdown

0

Non dimenticare, per le stringhe, puoi sempre usare:

String.IsNullOrEmpty(str)

Invece di:

str==null || str==""

1
La domanda viene confrontata con "0" non una stringa vuota.
dance2die

-1

Un passo avanti rispetto alla bella risposta di Joshua Shannon . Ora con la prevenzione di boxe / 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));
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.