.Net 7 的Native AOT为啥不支持反射,你知道吗
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.Net 7 的Native AOT为啥不支持反射,你知道吗相关的知识,希望对你有一定的参考价值。
楔子
Native AOT为啥不支持反射呢,问题萦绕脑海?微软官方说不支持运行时生成的代码,这很难让人彻底理解它。
本篇从源码层面来解读下。
代码
上一段简单的代码例子
public class A
public void FirstMethod()
static void Main(string[] args)
A a = new A();
Type type =a.GetType();
MethodInfo[] methodinfo= type.GetMethods();
foreach (MethodInfo method in methodinfo)
Console.WriteLine(method.Name);
Debug
一:对上面的代码进行ILC调试的时候,ILC控制台直接报错,因为用到了反射,具体错误如下:
这个错误很明显是它告诉你调用了
System.Type.GetMethods()这个函数导致的。而这个函数刚好就是反射获取方法。那么这个错误从何而来呢?
二:为了研究这个错误,可以整个ILC项目搜索下这个错误的字符串,其中有一个字符串:Trim analysis warning可以找到错误的结果。搜索它之后,很快在Logger.cs这个文件里面找到了相同的字符串。而这个字符串在Logger.cs类的一个方法名称叫做:ShouldSuppressAnalysisWarningsForRequires里面。直接在里面下断点。
果然断点跳转到了它里面去,如下图所示:
三:更进一步,通过调试找到了类:
TrimAnalysisPatternStore。这个类里面有个方法:
MarkAndProduceDiagnostics,如下图所示:
可以看到,这个方法里MethodCallPatterns.Values 的循环, MethodCallPatterns.Values就是Program.Main方法里面所有调用的方法。其中就包括了反射System.Type.GetMethods。
四:它循环到System.Type.GetMethods,然后进入到MarkAndProduceDiagnostics函数。这个函数经过一系列调用,进入到了HandleCallAction类,调用了里面的Invoke方法。在HandleCallAction类的1145行处,它会判断GetMethods这个方法是否是静态方法,如果不是,则调用
_requireDynamicallyAccessedMembersAction.invoke里面会判断方法。如下图所示:
五:看一个函数
_requireDynamicallyAccessedMembersAction.invoke里面通过
Annotations.SourceHasRequiredAnnotations方法判断
GetMethods是否是被
DynamicallyAccessedMembersAttribute特性标记,如果标记了返回
true.最后调用_diagnosticContext.AddDiagnostic写入最上面报错的那段话。
Annotations.SourceHasRequiredAnnotations函数原型如下:
public static bool SourceHasRequiredAnnotations (
DynamicallyAccessedMemberTypes sourceMemberTypes,
DynamicallyAccessedMemberTypes targetMemberTypes,
out string missingMemberTypesString)
missingMemberTypesString = string.Empty;
//获取反射GetMethods的特性
var missingMemberTypes = GetMissingMemberTypes (targetMemberTypes, sourceMemberTypes);
//特性如果等于空,则返回true。由于System.Type.GetMethods()函数的特性不为空所以这里返回false,也刚好调用了上面的_diagnosticContext.AddDiagnostic,在控制台上报错。
if (missingMemberTypes == DynamicallyAccessedMemberTypes.None)
return true;
missingMemberTypesString = GetMemberTypesString (missingMemberTypes);
return false;
托管和非拖的反射
System.Type.GetMethods()方法原型如下:
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
public MethodInfo[] GetMethods()
return this.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
// Token: 0x060009AA RID: 2474
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
public abstract MethodInfo[] GetMethods(BindingFlags bindingAttr);
可以看到它的特性DynamicallyAccessedMembers,跟上面分析相印证。
GetMethods()这个函数最后会调用
System.RuntimeType.GetMethods。其原型如下:
public override MethodInfo[] GetMethods(BindingFlags bindingAttr)
return this.GetMethodCandidates(null, -1, bindingAttr, CallingConventions.Any, null, false).ToArray();
这个跟踪下去最后调用的是FCall/QCall。
结尾
总结下,就是ILC会判断这个方法是否被标记了DynamicallyAccessedMembersAttribute特性,并且是否调用了FCall/QCall。如果满足以上条件,则进行ILC代理,直接写入错误日志,导入到控制台上。
作者:江湖评谈(公众号同名)专研.Net 7的CLR和ILC核心技术。包括并不局限于:Roslyn,RyuJIT,二进制,汇编,C/C++。版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
以上是关于.Net 7 的Native AOT为啥不支持反射,你知道吗的主要内容,如果未能解决你的问题,请参考以下文章