我正在创建一个将小脚本转换为dll的系统。当我尝试使用可空值类并将其作为参数的默认值时,我遇到了一个问题。问题是我需要在编译器中创建一个user selected nullable的实例,并将其设置为常量。
不幸的是,每当我使用Activator.CreateInstance(NullableType)
(其中NullableType是从用户输入创建的类型)时,我得到null
作为返回值。例如,简单地执行:
object Test = Activator.CreateInstance(typeof(Nullable<int>));
Console.WriteLine(Test == null);
返回true。这种只发生在中。c#中创建的结构体,即使是泛型结构体,也可以创建得很好。哎呀,如果我复制&从DotNetReflector粘贴nullable(并删除TypeDependencyAttribute, TargetPatchingOptOutAttribute,和ThrowHelper调用,因为我没有访问这些),它显示上面的False。
怎么回事?是否有任何其他可能的方法来创建一个空当我不知道的通用参数,直到运行时?
From this MSDN blog:
活化剂。永远不能期望CreateInstance返回null之前;使用此DCR,在创建实例时将返回nulltype可为空但不提供非空T值。例如,Activator.CreateInstance(typeof(Int32?))返回null.
这个问题与Nullable<T>
的变量如何装箱有关,正如博客文章所解释的。
问题是在ParameterBuilder.SetConstant
,而不是Activator.CreateInstance
。SetConstant
是定义可选参数默认值的函数,需要提供一个具体的值作为常量。对于ref类,null
是一个有效的具体值,但是对于创建Nullable<>
之前的值类,null
不是一个有效值。例如,如何将null
拆成int
?因此,SetConstant
检查值类型,看看作为常量传入的具体值是否为null
,并抛出ArgumentException
,作为一个更具描述性的错误,而不是将null拆箱到值类中所得到的NullReferenceException
。
在Nullable<>
的情况下,null
现在是值类的有效具体值。Nullable<>
本身是一个值类,就像Activator.CreateInstance
一样,将null
拆箱到Nullable<int>
中是有意义的。这就是SetConstant
有一个bug的地方:它没有考虑到这一点,并抛出了一个"描述性"错误,而实际上并不是一个错误。代替微软的错误修复,任何使用空Nullable<>
调用SetConstant
将不得不实现由不正确的条件保护的行为。这意味着使用反射挖掘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
});
我已经向微软报告了这个bug。他们回应说,这个问题不会在。net 3.5中修复,但它被添加到内部错误数据库中。
更新:
该错误已在。net 4.0中修复。ParameterBuilder.SetConstant
现在有一个constant == null
条件的分支,检查值类型是否是从Nullable<>
派生的泛型,如果不是,则抛出异常。