为什么Nullable <t>可以为空?为什么不能再现?</t>

时间:2012-09-18 11:59:06

标签: c# nullable

写作时

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,但它只是说不可能没有任何答案从根本上说明为什么不可能。

3 个答案:

答案 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设置为truetest.Value.HasValuefalse?由于对非可空类型参数的限制,这种混淆不会发生。

最后,null作业与Nullable<T>一起使用,因为 - 正如所选答案及其对this SO questionthis 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支持使它们不是递归的。

  • CLR支持特殊的装箱/取消装箱规则int? i = 1; object o = i会将int值放入变量o而不是Nullable<int>值。如果出现多个空格 - o = (int??)1;是否应包含intint?值?
  • CLR特别支持调用GetType和接口成员 - 它调用底层类型的方法。当Nullable.GetType()具有NullValueFlag时会抛出NullObjectReference异常,这实际上会导致这种情况。

对于C#,C#中有许多功能可以为可空类型进行硬编码。 基于本文Nullable Types (C# Programming Guide),引入可空类型的主要目的是为不支持空值的类型添加null支持。逻辑上,自DateTime?已经支持空值,不应该让它“更”可空。

本文件也明确指出

  

不允许使用嵌套的可空类型。以下行不会编译:{{1​​}}

可空类型的特殊C#功能:

  • C#特别?运营商。 Nullable<Nullable<int>> n;应解析为(int???)null ?? (int)1还是(int??)1值?
  • Nullables具有特殊的(int)1属性。它应该为嵌套的nullables返回什么?
  • System.Nullable.GetValueOrDefault? == null运营商的特殊处理。如果? != null包含Nullable<Nullable<T>>值,但此值为null,那么HasValue属性应该返回什么?与null比较的结果应该是什么?
  • 特殊隐式转化。 Nullable<T>是否可以隐式兑换?
  • 明确转换。应该int?? i = 10支持吗?
  • 对bool的特别支持?输入Using nullable types。例如。 int i = (int??)10;

那么,CLR是否应该支持递归GetType()调用?它应该在装箱值时删除Nullable包装吗?如果它应该用于一个级别的值,为什么不用于所有其他级别呢?要考虑的选项太多,递归处理太多。

最简单的解决方案是使(bool?)null | (bool?)true == true不可编辑。