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

时间:2012-01-01 02:47:20

标签: c# reflection

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

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

object Test = Activator.CreateInstance(typeof(Nullable<int>));
Console.WriteLine(Test == null);

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

发生了什么事?当我在运行时之前不知道通用参数时,还有其他可能的方法来创建可空吗?

2 个答案:

答案 0 :(得分:6)

来自this MSDN blog

  

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

问题与Nullable<T>的变量如何装箱,如博客文章所述。

答案 1 :(得分:2)

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

对于Nullable<>null现在是值类的有效具体值。 Nullable<>本身就是一个价值类,如Activator.CreateInstance所示,而null中的取消装箱Nullable<int>具有意义。这是SetConstant有错误的地方:它没有考虑到这一点,并且对于实际上不是错误的内容抛出了“描述性”错误。代替来自Microsoft的错误修正,对SetConstant的任何空Nullable<>的调用都必须实现由不正确的条件保护的行为。这意味着使用反射挖掘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
});

我向微软报告了这个错误。他们回答称它不会在.NET 3.5中修复,但它被添加到内部错误数据库中。

<强>更新

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