Подтвердить что ты не робот

С#: Альтернатива GenericType == null

Мне нужно проверить общий объект на null или по умолчанию (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 и значение по умолчанию (T) очень часто равно null...

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

4b9b3361

Ответ 1

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

object.Equals(value, default(T))

Ответ 2

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

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()), поскольку он иногда затрудняет чтение.

Ответ 3

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

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));

}

Ответ 4

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

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

Edit:

По-видимому, существует статический метод object.Equals, о котором я не знал:

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

Это лучше.

Ответ 5

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

    static bool IsNullOrDefault<T>(T value)
    {
        return ((object)default(T)) == null ?
            ((object)value) == null :
            default(T).Equals(value);
    }

Ответ 6

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

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

  • Объявить приватную статическую переменную только для чтения с именем isDefault типа Predicate<T> в вашем общем классе
  • Добавить статический инициализатор в ваш общий класс, где вы проверяете T nullability, и установите isDefault на v==null или default(T).Equals(v) в зависимости от результата
  • Используйте 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);
    }

}

Ответ 7

С тестами:

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)); }
}

Ответ 8

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

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

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