Типы, допускающие значение NULL: лучший способ проверить нулевое или нулевое значение в с #

85

Я работаю над проектом, в котором я проверяю следующее во многих-многих местах:

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

больше из любопытства, чем что-либо еще, как лучше всего проверить оба случая?

Я добавил вспомогательный метод:

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

Есть ли способ лучше?

гвоздь
источник

Ответы:

162

мне нравится if ((item.Rate ?? 0) == 0) { }

Обновление 1:

Вы также можете определить метод расширения, например:

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

И используйте это так:

if(item.IsNullOrValue(0)){} // но от этого мало что получится

eglasius
источник
3
спасибо - очень лаконично! Я был обеспокоен удобочитаемостью, но пришел к выводу, что он был бы отлично читаемым, если бы я действительно понял ?? оператор.
nailitdown
2
Вам следует использовать метод расширения; хотя в момент написания он читается, эти маленькие фрагменты кода требуют небольшого размышления, а это означает, что если вы попытаетесь прочитать код, а он его использует, вы отвлечетесь от своей основной проблемы.
конфигуратор
11
@nailitdown: не совсем, вам просто нужен метод расширения для Nullable <T>. двойной? является псевдонимом для Nullable <double>. Ваша подпись будет выглядеть так: public static bool IsNullOrValue <T> (this Nullable <T>, t valueToCheck), где T: struct
Джошуа Шеннон
3
@nailitdown: (часть II) Тогда ваш оператор return будет выглядеть как return (value ?? default (T)). Equals (valueToCheck);
Джошуа Шеннон,
2
Если вы сократите его до «IsNullOr (0)», это будет естественно читать как «ноль или ноль»
ChrisFox
41

Использование дженериков:

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

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

Если Tэто ссылочный тип , valueбудет сравниваться с null( default(T)), в противном случае, если Tэто a value type, скажем, double, default(t)это 0d, для bool есть false, для char '\0'и так далее ...

Кристиан К. Сальвадо
источник
1
Метод расширения:public static bool IsNullOrValue<T>(this T? value, T valueToCheck) where T : struct { return (value ?? default(T)).Equals(valueToCheck); }
Behzad Ebrahimi
39

Хотя мне очень нравится принятый ответ, я думаю, что для полноты следует упомянуть и этот вариант:

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

Это решение


¹ Однако это не должно повлиять на ваше решение, поскольку такие виды микрооптимизации вряд ли что-то изменит.

Heinzi
источник
19

Это действительно просто расширение принятого ответа Фредди Риоса только с использованием 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));
}

ПРИМЕЧАНИЕ: нам не нужно проверять значение по умолчанию (T) на null, поскольку мы имеем дело либо с типами значений, либо со структурами! Это также означает, что мы можем с уверенностью предположить, что T valueToCheck не будет нулевым; Помните здесь, что T? является сокращением Nullable <T>, поэтому, добавляя расширение к Nullable <T>, мы получаем метод в int ?, double ?, bool? и т.п.

Примеры:

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

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

bool? z = false;
z.IsNullOrDefault(); //true
Джошуа Шеннон
источник
2

Я согласен с использованием ?? оператор.

Если вы имеете дело со строками, используйте if (String.IsNullOrEmpty (myStr))

Ник Йозевски
источник
2

Есть ли способ лучше?

Что ж, если вы действительно ищете лучший способ, вы, вероятно, можете добавить еще один уровень абстракции поверх Rate. Вот кое-что, что я только что придумал, используя Nullable Design Pattern.

используя Систему;
using System.Collections.Generic;

пространство имен NullObjectPatternTest
{
    Программа публичного класса
    {
        public static void Main (string [] args)
        {
            var items = новый список
                            {
                                новый предмет (RateFactory.Create (20)),
                                новый элемент (RateFactory.Create (null))
                            };

            PrintPricesForItems (элементы);
        }

        private static void PrintPricesForItems (элементы IEnumerable)
        {
            foreach (элемент var в элементах)
                Console.WriteLine ("Цена предмета: {0: C}", item.GetPrice ());
        }
    }

    открытый абстрактный класс ItemBase
    {
        общедоступная аннотация Rate Rate {get; }
        public int GetPrice ()
        {
            // НЕ нужно проверять, если Rate == 0 или Rate == null
            return 1 * Rate.Value;
        }
    }

    открытый класс Item: ItemBase
    {
        частный только для чтения Rate _Rate;
        публичное переопределение Rate Rate {get {return _Rate; }}
        общедоступный элемент (ставка) {_Rate = ставка; }
    }

    общедоступный запечатанный класс RateFactory
    {
        public static Rate Create (int? rateValue)
        {
            если (! rateValue || rateValue == 0) 
                return new NullRate ();
            вернуть новую ставку (rateValue);
        }
    }

    публичный класс Ставка
    {
        public int Value {получить; набор; }
        общедоступный виртуальный логический объект HasValue {получить {возврат (значение> 0); }}
        public Rate (int value) {Value = value; }
    }

    открытый класс NullRate: Rate
    {
        общественное переопределение bool HasValue {получить {вернуть ложь; }}
        public NullRate (): base (0) {}
    }
}
dance2die
источник
1
я думаю, вы правы, что удаление нулевых значений на каком-то более раннем этапе было бы правильным
решением
Не совсем. Есть такое понятие, как «Рефакторинг». Вы можете реорганизовать свой код в сторону лучшего шаблона или лучшей структуры. Вы всегда можете удалить значения, допускающие значение NULL, на более поздних этапах.
dance2die
2

Образец кода не удастся. Если obj имеет значение null, obj.ToString () приведет к исключению нулевой ссылки. Я бы сократил процесс и проверил наличие нулевого объекта в начале вашей вспомогательной функции. Что касается вашего фактического вопроса, какой тип вы проверяете на ноль или ноль? В String есть отличная функция IsNullOrEmpty, мне кажется, это было бы отличным использованием методов расширения для реализации метода IsNullOrZero в int? тип.

Изменить: Помните, "?" - это просто сахар компилятора для типа INullable, поэтому вы, вероятно, можете взять INullable в качестве parm, а затем jsut сравнить его с нулем (parm == null), а если не null, сравнить с нулем.

Вальден Леверих
источник
Приветствую вас за обнаружение этой ошибки - в этом проекте мне нужно проверить int ?, double?, decimal? и т. д., поэтому я использую "объект"
nailitdown
Помните "?" - это просто сахар компилятора для типа INullable <T>, поэтому вы, вероятно, можете взять INullable <T> в качестве parm, а затем jsut сравнить его с нулем (parm == null) и, если не null, сравнить с нулем.
Уолден Леверих,
0
public static bool nz(object obj)
{
    return obj == null || obj.Equals(Activator.CreateInstance(obj.GetType()));
}

источник
1
Отражение происходит медленно , будьте осторожны с подобными решениями. Я не возражаю против отражения, но я бы не ожидал его в такой "простой" функции, как эта, и это застало бы меня врасплох.
Уолден Леверих,
это действительно проверка на ноль?
nailitdown
Вообще-то, нет. Я полагаю, Activator.CreateInstance (obj.GetType ()) вернет null для типов, допускающих значение NULL.
конфигуратор
@configurator: когда типы, допускающие значение NULL, помещаются в коробку, они упаковываются как базовая структура, т. е. таким образом создается не структура с нулевым значением, допускающая значение NULL, а структура со значением по умолчанию, не допускающая значения NULL.
Эмон Нербонн,
0
class Item{  
 bool IsNullOrZero{ get{return ((this.Rate ?? 0) == 0);}}
}
dtroy
источник
ваше решение работает для одного свойства, я должен был указать, что у меня есть много свойств одного и того же элемента для проверки на ноль / ноль
nailitdown
0

Не забывайте, что для строк всегда можно использовать:

String.IsNullOrEmpty(str)

Вместо:

str==null || str==""
Крис
источник
1
Вопрос сравнивается с "0" не пустой строкой.
dance2die