You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

反射调用抛出异常的构造函数遇到的问题:以System.RuntimeType为例

解决RuntimeType内部构造函数反射调用时对象被丢弃的问题

首先得澄清一个关键点:当构造函数抛出异常时,CLR其实不会返回一个完全有效的对象——你提到的“普通情况下对象仍会被构造”可能是一种误解,或者是某些调试场景下的特殊表现。不过回到你的核心问题:调用internal修饰的RuntimeType构造函数时,反射调用抛出的TargetInvocationException会包裹原始异常,同时未完成构造的实例会被直接丢弃,这确实是反射API的默认行为。

为什么会出现这种差异?

当你直接调用构造函数(如果能访问的话),CLR会先分配对象内存,再执行构造逻辑——如果构造函数中途抛异常,内存里的实例可能还存在,但它并没有完成初始化,也不会被返回给调用者;而反射调用(比如ConstructorInfo.Invoke()或Activator.CreateInstance())的框架逻辑会在构造失败后自动清理这个未完全初始化的对象,因为反射API默认假设:构造失败的实例是不可用的,不应该被传递到调用层。

如何获取这个未完全构造的实例?

如果你确实因为调试或研究需要拿到这个实例(强烈不建议在生产代码中这么做,因为未初始化的对象会处于不稳定状态),可以试试这两种方法:

1. 使用FormatterServices.GetUninitializedObject创建未初始化实例

.NET官方提供了这个API,可以直接创建一个不调用构造函数的对象实例,之后再手动调用构造函数:

using System;
using System.Reflection;
using System.Runtime.Serialization;

Type runtimeType = typeof(RuntimeType);
// 创建未初始化的实例(不执行构造函数)
object instance = FormatterServices.GetUninitializedObject(runtimeType);

try
{
    // 获取内部构造函数并调用
    ConstructorInfo ctor = runtimeType.GetConstructor(
        BindingFlags.Instance | BindingFlags.NonPublic, 
        null, 
        Type.EmptyTypes, 
        null);
    ctor.Invoke(instance, null);
}
catch (TargetInvocationException ex)
{
    // 捕获包装的原始异常
    Console.WriteLine($"构造函数抛出异常:{ex.InnerException.Message}");
    // 此时instance已经存在,可继续操作(但风险极高)
}

2. 非托管代码手动分配内存并调用构造函数

这种方法更底层,直接绕开反射的包装逻辑,但危险性也更高:

using System;
using System.Reflection;
using System.Runtime.InteropServices;

public static unsafe object CreateInstanceWithRawMemory(Type type)
{
    // 手动分配对象内存
    IntPtr objPtr = Marshal.AllocHGlobal(Marshal.SizeOf(type));
    try
    {
        // 获取构造函数的方法指针
        ConstructorInfo ctor = type.GetConstructor(
            BindingFlags.Instance | BindingFlags.NonPublic, 
            null, 
            Type.EmptyTypes, 
            null);
        IntPtr ctorFuncPtr = ctor.MethodHandle.GetFunctionPointer();
        
        // 转换为委托并直接调用构造函数
        var ctorDelegate = (Action<IntPtr>)Marshal.GetDelegateForFunctionPointer(ctorFuncPtr, typeof(Action<IntPtr>));
        ctorDelegate(objPtr);
        
        // 将指针转换为对象实例
        return Marshal.PtrToStructure(objPtr, type);
    }
    catch (Exception ex)
    {
        Console.WriteLine($"构造异常:{ex.Message}");
        // 即使构造失败,内存中的实例依然存在
        return Marshal.PtrToStructure(objPtr, type);
    }
    finally
    {
        // 注意:不要释放内存,返回的对象还在使用
        // Marshal.FreeHGlobal(objPtr);
    }
}

最后提醒

RuntimeType是.NET运行时的核心内部类型,它的构造函数抛出NotSupportedException就是明确的信号:设计上就不允许用户直接实例化它。除非你在做极端的调试或研究工作,否则千万不要尝试实例化这个类型,否则可能导致不可预测的运行时问题。

内容的提问来源于stack exchange,提问作者MrLore

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:17:43