Activator.CreateInstance创建的是类型T的值而不是Nullable<T>。

9
请看下面的示例代码。
var genericNullableType = typeof(Nullable<>);
var nullableType = genericNullableType.MakeGenericType(typeof(bool));
var returnValue = Activator.CreateInstance(nullableType, (object)false);

由于某种原因,returnValue变量的类型将是bool而不是bool?。为什么会这样,如何避免?

更新:这是我在VS中的屏幕截图 enter image description here


@petelids 我正在使用VS 2013 Premium和.NET Framework 4.5,并且构建配置是Debug。你使用相同的配置吗? - Demarsch
1
这不仅适用于 ActivatorConsole.WriteLine((new Nullable<bool>(false).GetType().FullName)); 也只是输出普通的布尔值。 - vcsjones
1
无法获取可空的装箱值。运行时对可空类型的装箱有特殊处理,它将始终将其解包为基础类型的装箱值或 null 引用。话虽如此,它应该可以转换为 bool?,所以我不确定您正在寻找什么。 - Mike Zboray
2
这是Brad Wilson的相关博客文章[在不知道T的情况下创建Nullable<T>]。 - Habib
1个回答

15
在这种情况下,您使用了CreateInstance的重载版本,它返回objectNullable<T>是一个struct,因此要表示为object,它需要被装箱。但是,根据CLR的规则,Nullable<T>实际上无法被装箱。相反,使用底层值或null。这就是为什么您在这里得到一个原始的bool而不是bool?

文档:https://msdn.microsoft.com/en-us/library/ms228597.aspx

编辑

似乎有些混淆,无法确定值的类型是否可为空。特别是已指出以下内容打印System.Boolean而不是System.Nullable``1[System.Boolean]

var x = (bool?)true;
Console.WriteLine(x.GetType());

这段代码也存在装箱问题。调用 GetType 方法时,由于它是在 object 上调用一个虚方法而不是在 Nullable<T> 上调用一个方法,因此会发生隐式装箱操作:

IL_0009: ldloc.0
IL_000a: box valuetype [mscorlib]System.Nullable`1<bool>
IL_000f: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
IL_0014: call void [mscorlib]System.Console::WriteLine(object)

打印var值的实际类型最安全的方法是使用通用类型的以下技巧:

static void PrintType<T>(T value)
{
    Console.WriteLine(typeof(T));
}

PrintType(x);  // Prints System.Nullable`1[System.Boolean]

即使使用了CreateInstance的另一个重载,由于GetType是非虚拟的,所以它仍然会在内部装箱,因此您永远不会从GetType中看到Nullable<T>,对吗? - Mike Zboray
@mikez 正确。调用 GetType() 会生成一个隐式的 .box il 操作码,这会阻止它输出可空类型。要打印出真正的类型,您需要使用泛型。我马上就会演示。 - JaredPar

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