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

发布于 2024-12-23 10:01:16 字数 619 浏览 2 评论 0原文

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

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

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

返回 true。这种发生在可为 Nullable 的情况下。用 C# 创建的结构,即使是通用结构,也可以很好地创建。哎呀,如果我复制&从 DotNetReflector 粘贴可为 null(并删除 TypeDependencyAttribute、TargetPatchingOptOutAttribute 和 ThrowHelper 调用,因为我无权访问这些调用),它在上面显示 False。

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

I'm creating a system that turns a small script into a dll. I'm encountering a problem when I try to take a nullable value class and make it the default value of a parameter.
The problem is that I need to create an instance of user selected nullable within the compiler and set it as the constant.

Unfortunately, whenever I use Activator.CreateInstance(NullableType) (where NullableType is a created type from user input) I get null as the return value. For example, simply executing:

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

returns true. This ONLY happens with Nullables. A struct created in C#, even a generic one, is created fine. Heck, if I copy & paste nullable from DotNetReflector (and remove TypeDependencyAttribute, TargetPatchingOptOutAttribute, and ThrowHelper calls since I don't have access to those), it shows False above.

What is going on? Is there any other possible way to create a nullable when I don't know the generic parameters until run-time?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我爱人 2024-12-30 10:01:16

来自此 MSDN 博客

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

该问题与 Nullable 的变量如何装箱有关,如博客文章所述。

From this MSDN blog:

Activator.CreateInstance could never be expected to return null
before; with this DCR, it will return null when creating instance of
type Nullable but not providing non-null T value. For example,
Activator.CreateInstance(typeof(Int32?)) returns null.

The problem has to do with how variables of Nullable<T> are boxed as explained by the blog post.

南风起 2024-12-30 10:01:16

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

对于 Nullable<>null 现在是值类的有效具体值。 Nullable<> 本身是一个值类,如 Activator.CreateInstance 所示,并将 null 拆箱为 Nullable类型。 有意义。这就是 SetConstant 存在缺陷的地方:它没有考虑到这一点,并针对实际上不是错误的内容抛出“描述性”错误。代替 Microsoft 的错误修复,任何使用 null 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
});

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

更新:

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

The problem is in ParameterBuilder.SetConstant, not Activator.CreateInstance. SetConstant is a function for defining the default value of an optional parameter, and requires a concrete value be provided as the constant. For ref classes, null is a valid concrete value, but for value classes prior to the creation of Nullable<>, null was not a valid value. How do you unbox null into an int, for example? So SetConstant checked value types to see if the concrete value passed in as the constant was null and throws an ArgumentException as a more descriptive error than the NullReferenceException you'd get for unboxing a null into a value class.

In the case of a Nullable<>, null is now a valid concrete value for a value class. Nullable<> itself is a value class, as seen with Activator.CreateInstance, and unboxing null into a Nullable<int> has meaning. This is where SetConstant has a bug: it fails to take this into account and throws a 'descriptive' error for what isn't actually an error. In lieu of a bugfix from Microsoft, any call to SetConstant with a null Nullable<> will have to implement the behavior that's guarded by the incorrect conditional. This means digging into ParameterBuilder's private methods and fields using reflection. Here's the code I made to handle just this case. The standard SetConstant function should be used in situations that don't manifest the bug.

//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
});

I have reported the bug to Microsoft. They responded that it wouldn't be fixed in .NET 3.5, but it was added to the internal bug database.

UPDATE:

The bug has been fixed in .NET 4.0. ParameterBuilder.SetConstant now has a branch to the constant == null conditional that checks to see if a value type is a generic derived from Nullable<> and only throws the exception if it is not.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文