Nullable <T>的装箱/拆箱行为怎么可能?

Dan*_*Tao 32 .net c# vb.net boxing nullable

我今天早些时候发生的事情让我摸不着头脑.

Nullable<T>可以为任何类型的变量赋值null.例如:

int? i = null;
Run Code Online (Sandbox Code Playgroud)

起初,我看不出如何做到这一点是可能的,而不以某种方式从定义的隐式转换objectNullable<T>:

public static implicit operator Nullable<T>(object box);
Run Code Online (Sandbox Code Playgroud)

但是上面的运算符显然不存在,就好像它确实如此,那么下面的内容也必须是合法的,至少在编译时(它不是):

int? i = new object();
Run Code Online (Sandbox Code Playgroud)

然后我意识到,也许Nullable<T>类型可以定义一个隐式转换为一些永远无法实例化的任意引用类型,如下所示:

public abstract class DummyBox
{
    private DummyBox()
    { }
}

public struct Nullable<T> where T : struct
{
    public static implicit operator Nullable<T>(DummyBox box)
    {
        if (box == null)
        {
            return new Nullable<T>();
        }

        // This should never be possible, as a DummyBox cannot be instantiated.
        throw new InvalidCastException();
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,这并没有解释我接下来发生了什么:如果该HasValue属性是false针对任何Nullable<T>值的,那么该值将被装箱为null:

int? i = new int?();
object x = i; // Now x is null.
Run Code Online (Sandbox Code Playgroud)

此外,如果HasValuetrue,那么该值将被设置为a T而不是T?:

int? i = 5;
object x = i; // Now x is a boxed int, NOT a boxed Nullable<int>.
Run Code Online (Sandbox Code Playgroud)

但是,似乎暗示有从自定义隐式转换Nullable<T>object:

public static implicit operator object(Nullable<T> value);
Run Code Online (Sandbox Code Playgroud)

显然不是这种情况,object因为所有类型的基类都是如此,用户定义的基本类型的隐式转换是非法的(它们应该是非法的).

它似乎object x = i;应该i像任何其他值类型一样,因此x.GetType()产生与typeof(int?)(而不是抛出NullReferenceException)相同的结果.

所以我挖了一下,当然,事实证明这种行为是特定于Nullable<T>C#和VB.NET规范中特别定义的类型,并且在任何用户定义的struct(C#)或Structure(VB.NET )中都不可重现.).

这就是为什么我仍然感到困惑.

这种特殊的装箱和拆箱行为似乎无法手工实施.它只能起作用,因为C#和VB.NET都对该Nullable<T>类型进行了特殊处理.

  1. 从理论上讲,Nullable<T>在没有给予这种特殊处理的情况下,可能存在不同的基于CLI的语言吗?Nullable<T>因此,这种类型不会在不同语言中表现出不同的行为吗?

  2. C#和VB.NET如何实现这种行为?它是否受到CLR的支持?(也就是说,CLR是否允许类型以某种方式"覆盖"它的装箱方式,即使C#和VB.NET本身禁止它?)

  3. 甚至可以(在C#或VB.NET中)将a封装Nullable<T>object

Jon*_*eet 36

有两件事情发生了:

1)编译器将"null"视为空引用,而不是空 ...对于需要转换为的任何类型的null值.在Nullable<T>它的情况下,它只是HasValue字段/属性的值为False .因此,如果你有一个类型的变量int?,那么该变量的值很可能是null- 你只需要改变你对null一点点的理解.

2)拳击可空类型由CLR本身进行特殊处理.这与您的第二个示例相关:

    int? i = new int?();
    object x = i;
Run Code Online (Sandbox Code Playgroud)

编译器会将任何可空类型值与不可空类型值区别开来.如果该值不为null,则结果将int?与装入与非可空类型值相同的值相同 - 因此值为5的方式与int使用值5的方式相同- "可空性"将丢失.但是,可空类型的空值仅被设置为空引用,而不是根本创建对象.

这是在社区的要求下在CLR v2周期的后期引入的.

这意味着没有"盒装可空值 - 值类型"这样的东西.

  • 像往常一样 - 看IL代码,对象x = i被转换成指示CLR级支持的box指令. (2认同)