通过Activator.CreateInstance创建可为空的对象返回null

Kev*_*Fee 7 c# reflection

我正在创建一个将小脚本变成dll的系统.当我尝试获取可为空的值类并将其设置为参数的默认值时,我遇到了问题.问题是我需要在编译器中创建一个用户选择为nullable的实例,并将其设置为常量.

不幸的是,每当我使用Activator.CreateInstance(NullableType)(其中NullableType是来自用户输入的创建类型)时,我得到null返回值.例如,只需执行:

object Test = Activator.CreateInstance(typeof(Nullable<int>));
Console.WriteLine(Test == null);
Run Code Online (Sandbox Code Playgroud)

返回true.这与Nullables发生.在C#中创建的结构,甚至是通用结构,都可以很好地创建.哎呀,如果我从DotNetReflector复制并粘贴nullable(并删除TypeDependencyAttribute,TargetPatchingOptOutAttribute和ThrowHelper调用,因为我没有访问权限),它会在上面显示False.

到底是怎么回事?当我在运行时之前不知道通用参数时,还有其他可能的方法来创建可空吗?

M.B*_*ock 6

来自这篇MSDN博客:

永远不会期望Activator.CreateInstance返回null; 使用此DCR,它将在创建Nullable类型的实例时返回null,但不提供非空T值.例如,Activator.CreateInstance(typeof(Int32?))返回null.

这个问题与Nullable<T>博客文章中解释的盒装变量有关.


Kev*_*Fee 3

问题出在ParameterBuilder.SetConstant,不在Activator.CreateInstanceSetConstant是一个用于定义可选参数的默认值的函数,并且需要提供具体值作为常量。对于 ref 类,null是有效的具体值,但对于创建之前的值类Nullable<>null不是有效值。例如,如何将其拆箱null为?int因此,SetConstant检查值类型以查看作为常量传入的具体值是否会null抛出一个ArgumentException比将NullReferenceExceptionnull 拆箱到值类中时更具描述性的错误。

对于 a 来说Nullable<>null现在是值类的有效具体值。Nullable<>本身是一个值类,如 所示Activator.CreateInstance,拆箱null为 aNullable<int>有意义。这就是SetConstant存在错误的地方:它没有考虑到这一点,并针对实际上不是错误的内容抛出“描述性”错误。SetConstant任何使用 null 的调用都Nullable<>必须实现由错误条件保护的行为,而不是 Microsoft 的错误修复。这意味着使用反射来挖掘 的ParameterBuilder私有方法和字段。这是我为处理这种情况而编写的代码。标准SetConstant函数应该用在不会出现错误的情况下。

//ModuleBuilder module : a parameter passed into the containing function, the module which is being built (which contains the method that has the optional parameter we are setting the constant for)
//ParameterBuilder optParam : A parameter passed into the containing function, the parameter that is being built which is to have the null default value.
MethodInfo method = typeof(TypeBuilder).GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
    .Where(m => m.Name == "SetConstantValue" && m.GetParameters().Length > 0 && m.GetParameters()[0].ParameterType.Name == "RuntimeModule")
    .Single();
var RuntimeHandle = typeof(ModuleBuilder).GetMethod("GetNativeHandle", BindingFlags.NonPublic | BindingFlags.Instance);
method.Invoke(null, new object[]
{
    RuntimeHandle.Invoke(module, new object[]{}),
    optParam.GetToken().Token,
    0x12,
    null
});
Run Code Online (Sandbox Code Playgroud)

我已向 Microsoft 报告了该错误。他们回应称,该问题不会在 .NET 3.5 中得到修复,但已添加到内部错误数据库中。

更新:

该错误已在 .NET 4.0 中修复。ParameterBuilder.SetConstant现在有一个条件分支constant == null,用于检查值类型是否是派生自的泛型Nullable<>,如果不是,则仅抛出异常。