2012-09-18 88 views
3

當我寫爲什麼Nullable <T>可以爲空?爲什麼它不能被複制?

Nullable<Nullable<DateTime>> test = null;

我得到一個編譯錯誤:

The type 'System.Datetime?' must be a non-nullable value type in order to use it as a paramreter 'T' in the generic type or method 'System.Nullable<T>'

Nullable<T>是一個struct所以它應該是非空的。

所以我試圖創建此struct:當我寫

 Nullable<Foo<DateTime>> test1 = null; 
     Foo<Nullable<DateTime>> test2 = null; 
     Foo<DateTime> test3 = null; 

第一行是確定

public struct Foo<T> where T : struct 
{ 
    private T value; 

    public Foo(T value) 
    { 
     this.value = value; 
    } 

    public static explicit operator Foo<T>(T? value) 
    { 
     return new Foo<T>(value.Value); 
    } 

    public static implicit operator T?(Foo<T> value) 
    { 
     return new Nullable<T>(value.value); 
    } 
} 

,但現在第二行和第三行,我得到了以下兩個編譯錯誤:

The type 'System.DateTime?' must be a non-nullable value type in order to use it as a parameter 'T' in the generic type or method 'MyProject.Foo<T>'(僅限第二行)

Cannot convert null to 'MyProject.Foo<System.DateTime?> because it is a non-nullable value type'

 Foo<Nullable<DateTime>> test = new Foo<DateTime?>(); 

如果Nullable<DateTime>struct不工作也不事件。

概念,我可以理解爲什麼Nullable<T>是空的,它避免了像DateTime??????????東西,但是我仍然可以有List<List<List<List<List<DateTime>>>>> ...

那麼,爲什麼這個限制,爲什麼我不能重現Foo<T>這種行爲?這個限制是由編譯器執行還是在Nullable<T>代碼中的內在?

我讀this question但它只是說這是不可能的答案根本無法說出爲什麼這是不可能的。

+0

也許要通過Nullable 到Foo 你必須把Foo 這個錯誤只是告訴你DateTime?不能作爲T傳遞的參數,因爲T是不可空的。如果T引用不可爲空的類型,則可以爲空,並且永遠不適合該聲明。 – Amedio

回答

9

But Nullable is a struct so it's supposed to be non-nullable.

Nullable<T>確實是一個結構,但作爲docs規定的通用struct約束的確切含義是:

The type argument must be a value type. Any value type except Nullable can be specified. See Using Nullable Types (C# Programming Guide) for more information.

出於同樣的原因,你的行

Foo<Nullable<DateTime>> test2 = null; 

導致編譯器錯誤你看到,因爲你的通用struct約束限制你的通用T參數以某種方式,因此Nullable<DateTime>不得指定爲實際參數。

用於此的理由可能是撥打電話如

Nullable<Nullable<DateTime>> test = null; 

更加明確:這是否意味着你要設置test.HasValuefalse,或做你真正想要設置test.HasValuetruetest.Value.HasValuefalse?通過對非空類型參數的限制,這種混淆不會發生。

最後,null分配與Nullable<T>工作,因爲 - 作爲暗示的選擇答案和他們的意見,以this SO questionthis SO question - 在Nullable<T>類型由一些編譯器魔法的支持。

+0

闡述這種編譯器魔術可能會有所幫助,比如結構如何賦值並且可以與null相比。 –

+0

我感興趣的確是那個編譯器的魔力。它是如何工作的,以及這是不可重現的。 – Guillaume

+0

@Guillaume:好的,我認爲它是不可重現的,因爲它在編譯器中被硬編碼以將該魔術應用於'System.Nullable '而沒有其他任何東西。至於它的工作原理,您可能需要查看Mono的C#編譯器的源代碼。 –

1

錯誤是說Nullable的類型參數應該是不可空的。

你在做什麼,是創建具有可空類型參數可空類型,這是不允許的:

Nullable<Nullable<DateTime>> 

相同

Nullable<DateTime?> 

這是毫無意義。爲什麼你想要一個可爲空的類型的空類型?

Nullable只是在.NET 2.0中引入的一種類型,因此您可以使用「可空值類型」。例如,如果你有一個方法,它有一個可選的日期時間參數;而不是像DateTime.MinValue那樣傳遞一個'魔術值',如果你不想使用該參數,你現在可以將null傳遞給該方法。

+2

嗯,它可能沒有意義,但'0 + 0'也沒有意義。這是一個有效的程序。我認爲OP想知道是否有更多的根本原因爲什麼被明確禁止。 – usr

+0

如果不是'struct'約束,像'Nullable '這樣的東西就像'Dictionary '這樣的'TryGetValue'方法的邏輯返回類型,特別是如果'Nullable '很簡單暴露的領域結構沒有所有特殊的怪癖。在'foo = MyDict [「George」]'之後,如果'foo.HasValue'爲假,那就意味着找不到密鑰。如果'foo.HasValue'爲真,那意味着'Foo.Value'將保存與鍵相關的值。即使'foo.Value'表示爲null,這將有用地區別於「未找到鍵」。 – supercat

1

通用類where T: struct表示類型T不能爲空。 但可以使用可空類型來爲結構添加可空性。從技術上講,它們是結構,但它們的行爲就像它們可能包含空值。由於這種含糊不清的情況,禁止使用可空條約where T: struct約束 - 請參閱Constraints on Type Parameters

可空類型不僅僅是具有特殊C#編譯器支持的通用結構。 CLR本身支持可空類型(請參閱由Jeffrey Richter通過C#編寫的CLR),並且看起來像這種特殊的CLR支持使它們不是遞歸的。

  • CLR支持特殊裝箱/拆箱規則int? i = 1; object o = i會把int值成變量o而不是Nullable<int>值。如果有多個可空值 - 應該o = (int??)1;包含int還是int?的值?
  • CLR特別支持調用GetType和接口成員 - 它調用底層類型的方法。這實際上導致Nullable.GetType()拋出一個NullObjectReference異常時的情況,當它有NullValueFlag時。

至於C#,在C#中有很多功能是硬編碼爲可空類型的。 基於這篇文章Nullable Types (C# Programming Guide)介紹可空類型的主要目標是爲不支持空值的類型添加null支持。從邏輯上講,自從DateTime?已經支持空值,它不應該被允許爲「更多」空值。

這份文件也清楚地指出,

Nested nullable types are not allowed. The following line will not compile: Nullable<Nullable<int>> n;

可空類型的特殊C#特點:

  • C#有特殊?運營商。 (int???)null ?? (int)1應該解析爲(int??)1還是(int)1值?
  • 可疑物有特殊System.Nullable.GetValueOrDefault財產。它應該返回嵌套可空數據?
  • ? == null? != null經營者的特殊處理。如果Nullable<Nullable<T>>包含Nullable<T>值,但此值爲null,HasValue屬性應該返回什麼?什麼應該是與null比較的結果?
  • 特殊隱式轉換。 int?? i = 10應該可以隱式轉換嗎?
  • 顯式轉換。應該支持int i = (int??)10;
  • 特別支持bool?請輸入Using nullable types。例如。 (bool?)null | (bool?)true == true

那麼,CLR應該支持遞歸GetType()調用嗎?拳擊價值時它應該刪除可空包裝?如果它應該用於一個層次的價值,那麼爲什麼不爲所有其他層面呢?需要考慮的選項過多,遞歸處理過多。

最簡單的解決方案是使Nullable<Nullable<T>>不可編譯。

+0

Nah ...最簡單的解決方案根本不是將可空結構定義爲結構體,而是將其定義爲裝箱值類型,這在CLR和編譯器中自然允許使用更少的特殊情況。很好的解釋!這裏最好的答案。 – Qwertie

+0

@ Qwertie:另一種替代方法是要求將「可爲空的」的默認值稱爲「可爲空」。空'而不是'空',並且具有'可爲空的'框作爲可空的(其'HasValue'可能是真或假)。允許將'T'或'Nullable '從盒裝的'T'或'Nullable '拆箱,除非如果'Has'value'爲假,否則將'T'拆箱作爲'T'會引發異常。在這樣的規則下,不需要'T'上的'struct'約束。 – supercat

相關問題