反射调用Assembly.GetExecutingAssembly内部代码返回null的原因咨询
为什么通过反射调用RuntimeAssembly.GetExecutingAssembly会返回null?
先给大家还原一下问题场景:
根据.NET Framework的Assembly类源码,GetExecutingAssembly()内部是这么实现的:
public static Assembly GetExecutingAssembly() { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return RuntimeAssembly.GetExecutingAssembly(ref stackMark); }
当我们通过反射调用公开的Assembly.GetExecutingAssembly时,一切正常:
Type.GetType("System.Reflection.Assembly") .GetMethods(BindingFlags.Public | BindingFlags.Static) .Where(f => f.Name.Contains("GetCallingAssembly")) .First().Invoke(null, null)
但如果跳过公开方法,直接反射调用内部的RuntimeAssembly.GetExecutingAssembly,却始终返回null:
Type.GetType("System.Reflection.RuntimeAssembly") .GetMethods(BindingFlags.NonPublic | BindingFlags.Static) .Where(f => f.Name.Contains("GetExecutingAssembly")).Skip(1) // 第二个重载接收一个参数 .First().Invoke(null, new object[] { Type.GetType("System.Threading.StackCrawlMark") .GetEnumValues().GetValue(1) // System.Threading.StackCrawlMark.LookForMyCaller })
问题的核心原因
这个坑的根源在于ref参数的传递方式错误,以及StackCrawlMark的工作机制:
- ref参数的反射调用要求
你看,RuntimeAssembly.GetExecutingAssembly的方法签名是接收ref StackCrawlMark参数的,而不是普通值参数。在反射调用带ref/out修饰的方法时,你传递的不能只是一个枚举值的实例——你需要传递一个可被方法修改的引用变量。
你当前的代码只是把LookForMyCaller枚举值包装成对象数组传进去,但这是值传递,方法内部无法通过ref引用更新这个标记的状态,CLR也就没法正确追踪调用栈的上下文。
- StackCrawlMark的工作原理
StackCrawlMark是CLR用来定位调用栈位置的标记。当你调用公开的Assembly.GetExecutingAssembly时,方法会先初始化一个StackCrawlMark变量,然后以ref的方式传给内部方法——这样CLR才能在方法内部修改这个标记,找到当前执行代码所在的程序集。
而你直接反射调用时,没有满足ref参数的传递要求,CLR无法完成栈追踪的过程,自然返回null。
- 额外提醒:内部类型的调用风险
RuntimeAssembly是.NET Framework的内部类型,它的方法本来就不是设计给外部直接调用的,完全依赖CLR的内部调用约定。反射调用这类非公开内部方法本身就属于未被官方支持的场景,出现异常或者返回null都是意料之中的事。
内容的提问来源于stack exchange,提问作者SGKoishi
相关产品推荐
相关产品推荐

