C #: альтернатива GenericType == ноль - PullRequest
22 голосов
/ 19 февраля 2009

Мне нужно проверить универсальный объект на ноль или значение по умолчанию (T). Но у меня есть проблема ... В настоящее время я сделал это так:

if (typeof(T).IsValueType)
{
  if(default(T).Equals(thing))
    // Do something
  else
    // Do something else
}
else
{
  if(thing == null)
    // Do something
  else
    // Do something else
}

Но тогда я заканчиваю тем, что повторяюсь ... что мне не нравится. Проблема заключается в следующем:

thing == null;

Здесь ReSharper предупреждает о возможном сравнении типа значения с 'null'.

thing == default(T);

Здесь я получаю ошибку компилятора: невозможно применить оператор '==' к операндам типа 'T' и 'T'.

thing.Equals(null|default(T));

thing, очевидно, может быть нулевым (вот почему я должен проверять!), Поэтому вызовет исключение NullReferenceException.

null|default(T).Equals(thing);

null и default (T) также очень часто null ...

Есть ли чистый способ сделать это ??

Ответы [ 8 ]

53 голосов
/ 25 сентября 2009

Правильный способ сделать это:

return EqualityComparer<T>.Default.Equals(value, default(T))

Нет бокса. Вы даже можете определить метод расширения следующим образом:

public static void bool IsDefault<T>(this T value)
{
    return EqualityComparer<T>.Default.Equals(value, default(T));
}

.. и вызвать его так:

return entry.IsDefault();

Хотя мне лично наплевать на методы расширения для T (например, этот объект IsNull ()), поскольку иногда он затрудняет чтение

26 голосов
/ 19 февраля 2009

Если бокс не проблема, вы можете просто использовать:

object.Equals(value, default(T))
8 голосов
/ 10 февраля 2011

Когда мне нужно проверить значение NULL, я использую метод ниже. Обычно я использую это при вызове методов, которые принимают любой тип, но не нули, такие как Cache.

public static bool IsNull<T>(this T value)
{
    var type = typeof(T);

    return (type.IsClass || Nullable.GetUnderlyingType(type) != null) 
        && EqualityComparer<T>.Default.Equals(value, default(T));

}
2 голосов
/ 19 февраля 2009

Немного бокса отлично сработает.

    static bool IsNullOrDefault<T>(T value)
    {
        return ((object)default(T)) == null ?
            ((object)value) == null :
            default(T).Equals(value);
    }
2 голосов
/ 19 февраля 2009

Лучшая вещь, о которой я могу думать в данный момент:

return value == null || value.Equals(default(T));

Edit:

Очевидно, есть статический object.Equals метод, о котором я не знал:

return object.Equals(value, default(T));

Это лучше.

1 голос
/ 25 сентября 2009

Вы можете вообще избежать бокса, отметив, что обнуляемость типа может быть определена статически.

Вот что вы можете сделать:

  1. Объявите личную статическую переменную только для чтения с именем isDefault типа Predicate<T> в вашем родовом классе
  2. Добавьте статический инициализатор к вашему универсальному классу, где вы проверяете обнуляемость T, и устанавливаете isDefault либо v==null, либо default(T).Equals(v) в зависимости от результата
  3. Используйте isDefault(x) вместо x==null в остальном коде

Вот пример:

public class Example<T> {

    private static readonly Predicate<T> isDefault;

    static Example() {
        // Nullability check is a bit ugly, but we do it once per T,
        // so what the heck...
        if (typeof(T).IsValueType &&
           (!typeof(T).IsGenericType
        ||  typeof(T).GetGenericTypeDefinition() != typeof(Nullable<>)
        )) {
            // This is safe because T is not null
            isDefault = val => default(T).Equals(val);
        } else {
            // T is not a value type, so its default is null
            isDefault = val => val == null;
        }
    }

    public bool Check(T value) {
        // Now our null-checking is both good-looking and efficient
        return isDefault(value);
    }

}
0 голосов
/ 25 сентября 2009

Что с этим не так?

if (thing == default(T)) { }

Если это тип значения, JIT просто полностью удалит оператор.

0 голосов
/ 19 февраля 2009

с тестами:

public class DefaultOrNullChecker<T>  {
    public bool Check(object x) {
        return object.ReferenceEquals(x, null) || x.Equals(default(T));
    }
}
[TestFixture]
public class Tests {
    [Test]  public void when_T_is_reference_type() { 
        Assert.IsFalse(new DefaultOrNullChecker<Exception>().Check(new Exception()));}
    [Test] public void when_T_is_value_type() { 
        Assert.IsFalse(new DefaultOrNullChecker<int>().Check(123)); }
    [Test] public void when_T_is_null() {
        Assert.IsTrue(new DefaultOrNullChecker<Exception>().Check(null));}
    [Test] public void when_T_is_default_value() { 
        Assert.IsTrue(new DefaultOrNullChecker<int>().Check(0)); }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...