6

当我写

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>'

But Nullable<T>is astruct所以它应该是不可为空的。

所以我试图创建这个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>代码中固有的?

我读了这个问题,但它只是说不可能,没有一个答案从根本上说明为什么不可能。

4

3 回答 3

10

但是 Nullable 是一个结构,所以它应该是不可为空的。

Nullable<T>确实是一个结构,但文档struct中所述的通用约束的确切含义是:

类型参数必须是值类型。Nullable可以指定除此之外的任何值类型。有关更多信息,请参阅使用 Nullable 类型(C# 编程指南)

出于同样的原因,您的线路

Foo<Nullable<DateTime>> test2 = null;

导致您看到的编译器错误,因为您的泛型struct约束T以某种方式限制您的泛型参数,因此Nullable<DateTime>不得将其指定为实际参数。

这样做的理由可能是拨打电话,例如

Nullable<Nullable<DateTime>> test = null;

不那么模棱两可:这是否意味着您要设置test.HasValuefalse,或者您实际上是否要设置test.HasValuetrue和?由于对不可为空类型参数的给定限制,不会发生这种混淆。test.Value.HasValuefalse

最后,null分配工作是Nullable<T>因为 - 正如所选答案及其对这个 SO 问题这个 SO 问题的评论所暗示的那样- 该Nullable<T>类型受到一些编译器魔法的支持。

于 2012-09-18T12:05:11.260 回答
1

错误是说 Nullable 的类型参数应该是不可为空的。

您正在做的是创建一个 Nullable 类型,该类型具有可为空的类型参数,这是不允许的:

Nullable<Nullable<DateTime>>

是相同的

Nullable<DateTime?>

这是毫无意义的。为什么要为已经可以为空的类型设置一个可以为空的类型?

Nullable 只是 .NET 2.0 中引入的一种类型,因此您可以使用“可空值类型”。例如,如果您有一个方法,它的日期时间参数是可选的;如果您不想使用该参数,则现在可以将 null 传递给该方法,而不是传递像 DateTime.MinValue 这样的“神奇值”。

于 2012-09-18T12:04:58.763 回答
1

在泛型类where T: struct中意味着类型T不能为空。然而,可空类型旨在为结构添加可空性。从技术上讲,它们是结构,但它们的行为就像它们可能包含空值一样。由于这种歧义,约束不允许使用可空值where T: struct- 请参阅类型参数的约束

可空类型不仅仅是具有特殊 C# 编译器支持的通用结构。CLR 本身支持可空类型(请参阅 Jeffrey Richter 通过 C# 编写的 CLR),并且看起来这种特殊的 CLR 支持使它们成为非递归的。

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

至于 C#,C# 中有很多特性是为可空类型硬编码的。根据这篇文章Nullable Types (C# Programming Guide),引入可为 null 类型的主要目标是为不支持 null 的类型添加 null 支持。从逻辑上讲,从 DateTime? 已经支持空值,它不应该被允许“更多”为空。

该文件还明确指出

不允许嵌套可为空的类型。以下行将无法编译:Nullable<Nullable<int>> n;

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

  • C#有什么特殊的??操作员。应该(int???)null ?? (int)1下决心(int??)1还是(int)1重视?
  • Nullables 具有特殊的System.Nullable.GetValueOrDefault属性。它应该为嵌套的空值返回什么?
  • ? == null和操作员的特殊处理? != null。如果Nullable<Nullable<T>>包含Nullable<T>值,但该值为 null,则 HasValue 属性应返回什么?与null比较的结果应该是什么?
  • 特殊的隐式转换。应该int?? i = 10是隐式可转换的吗?
  • 显式转换。应该int i = (int??)10;支持吗?
  • 对 bool 的特殊支持?type使用可为空的类型。例如(bool?)null | (bool?)true == true

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

最简单的解决方案是使Nullable<Nullable<T>>不可编译。

于 2012-09-18T13:50:59.800 回答