2012-01-01 40 views
7

我创建变成一个小的脚本到DLL的系统。当我尝试使用可为空的值类并将其设置为参数的默认值时,我遇到了一个问题。 问题是我需要在编译器中创建一个用户选中的可为空的实例,并将其设置为常量。创建通过Activator.CreateInstance可为空的对象,则返回null

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

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

返回true。这只有与Nullables发生。在C#中创建的结构,即使是通用结构,也可以很好地创建。哎呀,如果我复制从DotNetReflector &膏可空(和删除TypeDependencyAttribute,TargetPatchingOptOutAttribute和ThrowHelper调用,因为我没有获得这些),它上面显示的是假。

这是怎么回事?在运行时我不知道泛型参数时,是否有其他可能的方法来创建空值?

回答

1

的问题是在ParameterBuilder.SetConstant,不Activator.CreateInstanceSetConstant是用于定义可选参数的默认值的函数,并且要求提供具体值作为常数。对于裁判班,null是一个有效的具体价值,但对于之前创造Nullable<>值类,null不是有效的值。例如,如何将null解锁为int?所以SetConstant检查值类型,看看是否传入的恒定具体值是null并引发ArgumentException为更具描述性的错误比NullReferenceException那么得到的拆箱空成值类。

对于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中修复,但它被添加到内部错误数据库中。

UPDATE:

该bug已被固定在.NET 4.0。 ParameterBuilder.SetConstant现在有一个跳转到constant == null的条件,该条件用于检查值类型是否是从Nullable<>派生的通用类型,如果不是,则仅引发该异常。

+0

模块和optParam指的是什么? – 2014-01-15 04:52:22

+0

1.非常疯狂的黑客攻击。 2.对象形式中的可空对象是空的 - 这是通过设计。 3.更多描述请。 – Vlad 2016-03-09 09:31:11

+0

是的,这是一个疯狂的黑客攻击。这是为了解决'ParameterBuilder.SetConstant'具有'if(value == null)抛出新的ArgumentException(...)'。 “ParameterBuilder.SetConstant”使用的实际本地代码(这是私有的,以及我在上面反映的)可以使用Nullables为空,因为它是有设计的。它只是一个包含错误的托管,面向公众的功能,并且需要绕过。这个错误已经被修复,所以这个答案不再是必须的,除非你的目标是3.5或更低。 – 2016-05-31 18:09:04

6

this MSDN blog

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

问题与Nullable<T>变量的方式如博客文章所解释的一样。

+0

这回答了我的两个问题之一。此外,在通过MSDN文章查看您的答案之前几分钟,我发现了这个答案:http://msdn.microsoft.com/en-us/library/ms228597.aspx。这留下了:如何在运行时不知道参数的情况下创建一个可为空的值,并将其传递给ParameterBuilder.SetConstant?即使将null null null传递给SetContstant也会因为博客和MSDN文章中引用的原因而失败。 – 2012-01-01 03:05:40

+0

我在我的研究中偶然发现了一个类似的问题。他们的情况虽然略有不同:http://stackoverflow.com/questions/1488706/set-property-nullable-by-reflection – 2012-01-01 03:12:43

+0

我不知道它是否会回答你的第二个问题,但你可以动态地创建你的泛型类型在运行时基于您的需求使用反射(http://stackoverflow.com/questions/2078914/c-sharp-dynamic-generic-type),并将结果的动态类型传递给Activator.CreateInstance,如您在问题中所示。我确实有过这方面的经验,但如果您需要帮助,请告诉我们。 – 2012-01-01 05:45:18