写作时
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
:
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);
}
}
现在我写的时候
Nullable<Foo<DateTime>> test1 = null;
Foo<Nullable<DateTime>> test2 = null;
Foo<DateTime> test3 = null;
第一行是正常的,但对于第二行和第三行,我得到以下两个编译错误:
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 :(得分:10)
但是Nullable是一个结构,所以它应该是不可为空的。
Nullable<T>
确实是一个结构,但docs中所述的通用struct
约束的确切含义是:
type参数必须是值类型。可以指定除
Nullable
之外的任何值类型。有关详细信息,请参阅Using Nullable Types (C# Programming Guide)。
出于同样的原因,你的行
Foo<Nullable<DateTime>> test2 = null;
导致您看到的编译器错误,因为您的通用struct
约束限制了您的通用T
参数,因此Nullable<DateTime>
不能被指定为实际参数。
这样做的理由可能是打电话,如
Nullable<Nullable<DateTime>> test = null;
不那么含糊:这是否意味着您要将test.HasValue
设置为false
,或者您确实要将test.HasValue
设置为true
和test.Value.HasValue
为false
?由于对非可空类型参数的限制,这种混淆不会发生。
最后,null
作业与Nullable<T>
一起使用,因为 - 正如所选答案及其对this SO question和this SO question的评论所暗示的那样 - Nullable<T>
类型一些编译器魔法支持。
答案 1 :(得分:1)
错误是说Nullable的type-parameter应该是不可为空的。
你正在做的是创建一个Nullable类型,它有一个可以为null的类型参数,这是不允许的:
Nullable<Nullable<DateTime>>
与
相同Nullable<DateTime?>
这是毫无意义的。为什么你想要一个可以为空的类型的可空类型?
Nullable只是.NET 2.0中引入的一种类型,因此您可以使用“可空值类型”。例如,如果你有一个方法,它的datetime-parameter是可选的;而不是传递像DateTime.MinValue这样的“魔术值”,如果你不想使用那个参数,你现在可以将null传递给该方法。
答案 2 :(得分:1)
在泛型类中where T: struct
表示类型T
不能为空。
但是,Nullable类型旨在为结构添加可空性。从技术上讲,它们是结构体,但它们的行为类似于它们可能包含空值。由于这种模糊性,where T: struct
约束不允许使用nullables - 请参阅Constraints on Type Parameters
Nullable类型不仅仅是具有特殊C#编译器支持的通用结构。 CLR本身支持可空类型(请参阅Jeffrey Richter通过C#的CLR),看起来这种特殊的CLR支持使它们不是递归的。
int? i = 1; object o = i
会将int
值放入变量o
而不是Nullable<int>
值。如果出现多个空格 - o = (int??)1;
是否应包含int
或int?
值?对于C#,C#中有许多功能可以为可空类型进行硬编码。 基于本文Nullable Types (C# Programming Guide),引入可空类型的主要目的是为不支持空值的类型添加null支持。逻辑上,自DateTime?已经支持空值,不应该让它“更”可空。
本文件也明确指出
不允许使用嵌套的可空类型。以下行不会编译:{{1}}
可空类型的特殊C#功能:
Nullable<Nullable<int>> n;
应解析为(int???)null ?? (int)1
还是(int??)1
值?(int)1
属性。它应该为嵌套的nullables返回什么?System.Nullable.GetValueOrDefault
和? == null
运营商的特殊处理。如果? != null
包含Nullable<Nullable<T>>
值,但此值为null,那么HasValue属性应该返回什么?与null比较的结果应该是什么?Nullable<T>
是否可以隐式兑换?int?? i = 10
支持吗?int i = (int??)10;
。那么,CLR是否应该支持递归GetType()调用?它应该在装箱值时删除Nullable包装吗?如果它应该用于一个级别的值,为什么不用于所有其他级别呢?要考虑的选项太多,递归处理太多。
最简单的解决方案是使(bool?)null | (bool?)true == true
不可编辑。