2009-02-19 45 views
22

我需要檢查一個泛型對象爲null或默認(T)。但我有一個問題...目前,我已經這樣做:C#:替代GenericType == null

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

空和默認(T)是經常空,以及...

有沒有乾淨的方式來做到這一點?

回答

26

如果拳擊不是一個問題,你可以只使用:

object.Equals(value, default(T)) 
2

我可以在那一刻想到的最好的事情是:

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

編輯:

顯然,有一個靜態object.Equals方法我不知道的:

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

這是更好。

+0

但如果value是一個值類型會發生什麼? – Svish 2009-02-19 14:42:42

+0

(value == null)將返回false。 (value.Equals(default(T))將檢查默認值 – configurator 2009-02-19 14:50:11

+0

這隻能避免拳擊,如果有一個Equals實現類型是專門化的,而不是默認的接受一個對象,鑑於此,我認爲object.Equals(value,default(T))更容易閱讀 – 2009-02-19 15:18:51

2

有點拳擊會做這項工作就好。

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

這不就是說對象嗎? .Equals(default(T),value)? – 2009-02-19 15:20:26

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

您可以注意到一個類型的可空完全避免拳擊能靜態確定。

這裏是你可以做什麼:

  1. 聲明一個私有靜態只讀在泛型類
  2. 稱爲Predicate<T>isDefault變量靜態初始化程序添加到您的泛型類,在這裏你檢查T的空性,並設置ISDEFAULT要麼v==nulldefault(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); 
    } 

} 
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

這是什麼問題?

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

如果它是一個值類型,那麼JIT將完全刪除該語句。

7

當我需要測試值是否爲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)); 

}