为什么 Nullable<T> 被认为是一个结构体而不是一个类?

11

我想定义一个通用类,可以接收任何可以设置为null的类型:

public abstract class BundledValue_Classes<T> 
    where T : class
{
    private Tuple<T, object> _bundle= new Tuple<T, object>();
    public T Value
    { 
        get { return _bundle == null ? null : _bundle.Item1; }
        set { _bundle = new Tuple<T, object>(value, obj); }
    }
    //...

这个很好用。我还想创建一个派生类,可以接受任何基本类型(int、double等),并将其解析为与上述类相同的内容,但其中基本类型被包装在Nullable中。

public abstract class BundledValue_Structs<T> 
    : BundledValue_Classes<T?> 
    where T : struct
{
   //...
}

但是由于某些原因,这并不起作用。我收到了错误信息:The type 'T?' must be a reference type in order to use it as parameter 'T' in the generic type or method 'BundledValue_Classes<T>'
你明白我在尝试做什么吗?我不想复制整个BundledValue_Classes<T>类的内容来处理包含在Nullable<T>中的基元情况。
也许有一种方法可以设置BundledValue_Classes<T>的类型约束,以便它不仅接受类,还接受任何可以分配空值的类型(包括Nullable<T>类型,这似乎是唯一的例外)。

1
附注:我强烈建议您遵循.NET命名约定。名称customTuple<T>没有这样做... - Jon Skeet
@JonSkeet 那只是我从类中删除了很多无用的内容,以使示例更清晰。 - Alain
给出干净、简短的示例是可以的,但如果忽略正常命名约定,则不会增加清晰度。 - Jon Skeet
为什么有Java标签?移除它... - Narendra Pathai
2个回答

17

Nullable<T>是一个结构体,根据文档所述。它并不是一个类,而是一个值类型,其字段看起来像这样:

public struct Nullable<T>
{
    private readonly T value;
    private readonly bool hasValue;
}

对于 Nullable<T> 类型的 null 值并不是一个空引用,它只是一个 hasValue 字段为 false 的值。
需要注意的是,Nullable<T> 不满足 where TFoo : struct 约束条件,因为这实际上是一个非可空值类型约束。基本上,Nullable<T> 既不满足 class 约束,也不满足 struct 约束。没有一种仅允许可空类型(引用或 Nullable<T>)的约束条件。

1
那么 Nullable<T> 除了具有特殊语法之外,还有什么神奇之处吗?它是一种既不是 : class 也不是 : struct 的类型? - user395760
1
@delnan: 它是一种既不满足这些类型约束的类型,同时还具有特殊的装箱行为。 - Jon Skeet
感谢@JonSkeet - 发布了一个后续问题,询问是否有解决重复类的方法:https://dev59.com/KnrZa4cB1Zd3GeqP0Uf4 - Alain
无需解决方法。请查看我对您后续问题的回复。Nullable<T>是一个结构体,但可空类型即int?double?,是满足<T>指定符的类型。 - drankin2112
1
@drankin2112: OP想要将类型参数限制为可空类型。你简单地无法这样做。我认为说“只是不要使用Bundled<float>”并不是一个好答案。 - Jon Skeet

1
如前所述,类型Nullable<T>是一个封装了T和一个指示其是否有效的标志的结构体。因为.NET的实现者们没有看到能够封装任何东西并指示其是否有效的类型有任何用处,所以他们决定不允许Nullable<Nullable<T>> [我不喜欢这个决定],而不是为Nullable<T>中的泛型参数提出一个特殊的约束条件,他们决定即使Nullable<T>是一个结构体,它也不应该满足struct类型约束。请保留html标签。
个人而言,我更希望看到Nullable<T>要么是一个具有类型为bool和无限制的T的公开字段的“普通”结构体,可以被例如Dictionary.TryGetValue使用来返回一个值并指示它是否有效(无论TValue是类、非可空结构还是Nullable<T>),要么是包含一个T的类。能够使用someNullable != null作为快捷方式的价值与其相比所引起的混淆和复杂性微不足道。尽管如此,.NET就是它的样子。个人建议一般避免使用可空类型,因为除非首先复制出值,否则无法对可空类型做任何事情。这些类型在线程安全方面也有一些奇怪的问题(例如,接收Nullable<T>的代码并观察到HasValue不能安全地假定GetValueOrDefault将返回Default(T),即使自从接收到Nullable<T>以来没有什么东西可能修改了它)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接