Tipos anuláveis: melhor maneira de verificar se há nulo ou zero em c #

85

Estou trabalhando em um projeto em que estou verificando o seguinte em muitos, muitos lugares:

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

mais como uma curiosidade do que qualquer outra coisa, qual é a melhor maneira de verificar os dois casos?

Eu adicionei um método auxiliar que é:

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

Existe uma maneira melhor?

Nailitdown
fonte

Respostas:

162

Eu gosto if ((item.Rate ?? 0) == 0) { }

Atualização 1:

Você também pode definir um método de extensão como:

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

E use-o assim:

if(item.IsNullOrValue(0)){} // mas você não consegue muito com isso

Eglasius
fonte
3
obrigado - muito sucinto! Fiquei preocupado com a legibilidade, mas cheguei à conclusão de que seria perfeitamente legível se eu realmente tivesse entendido o ?? operador.
nailitdown
2
Você deve usar o método de extensão; embora seja legível no momento da escrita, esses pequenos trechos de código requerem uma fração de pensamento, o que significa que se você tentar ler o código e ele o usar - você se distrairá do seu problema principal.
configurador
11
@nailitdown: na verdade não, você só precisa de um método de extensão para Nullable <T>. em dobro? é um alias para Nullable <double>. Sua assinatura será semelhante a: public static bool IsNullOrValue <T> (this Nullable <T>, t valueToCheck) onde T: struct
Joshua Shannon
3
@nailitdown: (parte II) Sua instrução de retorno seria então semelhante a return (value ?? default (T)). Equals (valueToCheck);
Joshua Shannon,
2
Se você encurtar para "IsNullOr (0)", será lido naturalmente como "é nulo ou zero"
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

Se Tfor um tipo de referência , valueserá comparado com null( default(T)), caso contrário, se Tfor a value type, digamos double, default(t)é 0d, para bool is false, for char is '\0'e assim por diante ...

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

Embora eu goste bastante da resposta aceita, acho que, para ser mais completo, essa opção também deve ser mencionada:

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

Esta solução


¹ Isso não deve influenciar sua decisão, pois esses tipos de microotimização provavelmente não farão qualquer diferença.

Heinzi
fonte
19

Esta é realmente apenas uma expansão da resposta aceita de Freddy Rios usando apenas 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 , não precisamos verificar default (T) para null, pois estamos lidando com tipos de valor ou estruturas! Isso também significa que podemos assumir com segurança que T valueToCheck não será nulo; Lembra aqui daquele T? é uma abreviação Nullable <T>, portanto, adicionando a extensão a Nullable <T> obtemos o método em int ?, double ?, bool? etc.

Exemplos:

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

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

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

Eu concordo em usar o ?? operador.

Se você estiver lidando com strings, use if (String.IsNullOrEmpty (myStr))

Nick Josevski
fonte
2

Existe uma maneira melhor?

Bem, se você realmente está procurando uma maneira melhor, provavelmente pode adicionar outra camada de abstração em cima de Taxa. Bem, aqui está algo que acabei de criar usando o padrão de design anulável.

using System;
using System.Collections.Generic;

namespace NullObjectPatternTest
{
    Programa de classe pública
    {
        public static void Main (string [] args)
        {
            var items = nova lista
                            {
                                novo item (RateFactory.Create (20)),
                                novo item (RateFactory.Create (null))
                            };

            PrintPricesForItems (itens);
        }

        private static void PrintPricesForItems (IEnumerable items)
        {
            foreach (item var em itens)
                Console.WriteLine ("Preço do item: {0: C}", item.GetPrice ());
        }
    }

    public abstract class ItemBase
    {
        public abstract Rate Rate {get; }
        public int GetPrice ()
        {
            // NÃO há necessidade de verificar se Rate == 0 ou Rate == null
            return 1 * Rate.Value;
        }
    }

    public class Item: ItemBase
    {
        private readonly Rate _Rate;
        public override Rate Rate {get {return _Rate; }}
        Item público (taxa de taxa) {_Rate = taxa; }
    }

    classe pública selada RateFactory
    {
        public static Rate Create (int? rateValue)
        {
            if (! rateValue || rateValue == 0) 
                retornar novo NullRate ();
            retornar uma nova taxa (rateValue);
        }
    }

    taxa de classe pública
    {
        public int Value {get; conjunto; }
        public virtual bool HasValue {get {return (Value> 0); }}
        Taxa pública (valor interno) {Valor = valor; }
    }

    public class NullRate: Taxa
    {
        public override bool HasValue {get {return false; }}
        public NullRate (): base (0) {}
    }
}
dance2die
fonte
1
Eu acho que você está certo que eliminar valores nulos em algum estágio anterior teria sido o caminho a percorrer
Nailitdown,
Não exatamente. Existe um conceito chamado "Refatoração". Você pode refatorar seu código em direção a um padrão melhor ou a uma estrutura melhor. Você sempre pode eliminar valores anuláveis ​​em estágios posteriores.
dance2die
2

Sua amostra de código falhará. Se obj for nulo, o obj.ToString () resultará em uma exceção de referência nula. Eu encurtaria o processo e verificaria se há um obj nulo no início da função auxiliar. Quanto à sua pergunta real, qual é o tipo que você está verificando para nulo ou zero? Em String há uma ótima função IsNullOrEmpty, parece-me que isso seria um ótimo uso de métodos de extensão para implementar um método IsNullOrZero no int? tipo.

Edit: Lembre-se, o '?' é apenas o açúcar do compilador para o tipo INullable, então você provavelmente poderia pegar um INullable como o parm e então compará-lo com null (parm == null) e se não for null comparar com zero.

Walden Leverich
fonte
gritos por pegar aquele bug - neste projeto, eu preciso verificar em relação a int ?, double ?, decimal? etc., é por isso que estou usando "object"
nailitdown
Lembre o '?' é apenas o açúcar do compilador para o tipo INullable <T>, então você provavelmente poderia tomar um INullable <T> como o parâmetro e então apenas compará-lo com nulo (parm == nulo) e se não for nulo comparar com zero.
Walden Leverich
0
public static bool nz(object obj)
{
    return obj == null || obj.Equals(Activator.CreateInstance(obj.GetType()));
}

fonte
1
A reflexão é lenta , cuidado com soluções como essa. Não me oponho à reflexão, mas não a esperaria em uma função "simples" como essa e me pegaria desprevenido.
Walden Leverich
isso realmente verifica se há zero?
nailitdown
Na verdade não. Activator.CreateInstance (obj.GetType ()) retornará null para tipos anuláveis, eu acredito.
configurador
@configurator: quando os tipos anuláveis ​​são encaixotados, eles são encaixotados como a estrutura subjacente, ou seja, isso não construirá uma estrutura nula de valores nulos, mas uma estrutura não anulável com valor padrão.
Eamon Nerbonne,
0
class Item{  
 bool IsNullOrZero{ get{return ((this.Rate ?? 0) == 0);}}
}
dtroy
fonte
sua solução funciona para uma propriedade, eu deveria ter especificado que tenho muitas propriedades do mesmo item para verificar se há nulo / zero
nailitdown
0

Não se esqueça, para strings, você sempre pode usar:

String.IsNullOrEmpty(str)

Ao invés de:

str==null || str==""
Chris
fonte
1
A pergunta se compara a "0" e não a uma string vazia.
dance2die
-1

Um passo adiante da boa resposta de Joshua Shannon . Agora com a prevenção de boxing / 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
fonte