3

好吧,这个问题可能看起来很奇怪。确实如此。
然而,我坚信这些技巧有助于理解语言和 .net 平台。

C# 编译器如何处理可空类型暗示了这个问题。

Nullable<T>是一个结构。但是编译器不是这个结构而是它持有的值或者只是空引用。

在盒装 Nullable 的情况下,拆箱将如何工作也很有趣。

Nullable<int> myInt = boxedStruct as Nullable<int>;

我的意思boxedStruct是,这不是一个装箱的 int,而是整个结构。

好吧,可能是在 CLR 级别对 Nullables 进行了不同的处理,因为我无法理解下面程序的输出。

class Program
{
    public static bool? Property { get; set; }

    static void Main(string[] args)
    {
        Property = true;

        var valueType = typeof (Program).GetProperty("Property").GetValue(null).GetType();
        var propType = typeof (Program).GetProperty("Property").PropertyType;

        Console.WriteLine("Value's type '{0}' is the same as type of the property '{1}' - '{2}'", valueType, propType, valueType == propType);
        Console.ReadKey();
    }
}

输出:

值的类型“System.Boolean”与属性“System.Nullable`1[System.Boolean]”的类型相同 - “False”

更新:

这里有什么规范(ECMA 335)说:

I.8.2.4 值的装箱和拆箱
...
如果值类型是可为空的类型——定义为值类型 System.Nullable 的实例化——结果是空引用或其类型 T 的 Value 属性的按位副本,取决于其 HasValue 属性(分别为 false 和 true)。

所以如果我理解正确的话。Nullable<T>strcut 不能在 .Net Framework 中装箱,不仅 C# 编译器不可能,CLR 也是如此。

4

2 回答 2

3

我猜想您要问的问题是:

您可以在Nullable<int>不产生空引用或装箱 int 值的情况下对值进行装箱,而是实际装箱Nullable<int>吗?

不。

于 2013-09-27T16:10:26.240 回答
1

是否可以欺骗 C# 编译器来装箱Nullable<T>

不,因为不是编译器进行装箱,而是 CLR。是的,可空类型的装箱是在 CLR 本身中专门处理的。

那么有没有办法在没有这种特殊处理的情况下装箱一个可为空的结构呢?也没有。

但是可以装箱另一个看起来像的结构Nullable<T>(确保它具有适当的布局),并在未记录的关键字和内存黑客的帮助下,让 CLR 认为装箱的对象实际上是Nullable<T>.
谨防!此代码应仅用于教育目的。我不能保证它可以在其他平台上工作或在不同的条件下工作,因为它依赖于托管对象的底层布局。

public static unsafe object BoxNullable<T>(T? nullable) where T : struct
{
    object scam = new NullableFake<T>(nullable);
    TypedReference tr = __makeref(scam);
    IntPtr typehandle = typeof(Nullable<T>).TypeHandle.Value;
    IntPtr* trstruct = (IntPtr*)&tr;
    **((IntPtr**)trstruct[0]) = typehandle;
    return scam;
}

struct NullableFake<T> where T : struct
{
    public readonly T? Value;

    public NullableContainer(T? nullable)
    {
        Value = nullable;
    }
}

NullableFake<T>结构具有与 相同的布局Nullable<T>,因此可以安全地将其用于装箱。TypedReference用于获取指向变量的指针(包含指向对象的指针),暴露对象本身。对象内存布局中的第一个值是指向对象类型的指针。覆盖它会导致 CLR 认为类型不同。

我不知道有什么更安全的方法。

于 2014-11-03T23:19:56.717 回答