C#:GenericType的替代== null

时间:2009-02-19 14:33:08

标签: c# generics 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显然可以为null(这就是我必须检查的原因!),因此会导致NullReferenceException。

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

null和default(T)通常也为空...

有干净的方法吗?

8 个答案:

答案 0 :(得分:53)

这样做的正确方法是:

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()),因为它有时会妨碍可读性。

答案 1 :(得分:26)

如果拳击不是问题,你可以使用:

object.Equals(value, default(T))

答案 2 :(得分:8)

当我需要测试值是否为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));

}

答案 3 :(得分:2)

目前我能想到的最好的事情是:

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

编辑:

显然,有一种我不知道的静态object.Equals方法:

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

这样更好。

答案 4 :(得分:2)

有点拳击可以完成这项工作。

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

答案 5 :(得分:1)

你可以通过注意静态确定类型的可空性来完全避免装箱。

以下是您可以做的事情:

  1. 在通用类中声明名为isDefault的名为Predicate<T>的私有静态只读变量
  2. 为您的通用类添加静态初始化程序,在其中检查T的可空性,并根据结果将isDefault设置为v==nulldefault(T).Equals(v)
  3. 在代码的其余部分中使用isDefault(x)代替x==null
  4. 以下是一个例子:

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

答案 6 :(得分:0)

通过测试:

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

答案 7 :(得分:-1)

这有什么问题?

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

如果它是值类型,那么JIT将完全删除该语句。