>> >> >> Reference << << << <<<<<<Ref>>>>>>
>> >> >> Indexer << << << <<<<<<Idx>>>>>>
Matched: 0

Tags

    Categories

      Types

        Top Results

          Reflection
          M: 2025-06-01 - ljf12825

          基本概念

          反射是一种计算机编程语言的能力,它允许程序在运行时(Runtime)检查、分析甚至修改自身的结构(如类、方法、属性、接口等)的行为
          反射像一面镜子
          在编写代码的编译时(Compiletime),你很清晰程序的结构
          而反射则让程序在运行时(Runtime)能够”照镜子“,看到自己由什么组成,并能动态地调用或操作这些部分\

          代码中有许多类,每个类都包含了许多字段,现在提供一个类Type,可以获得任何一个类的任何字段(包括私有字段),提供查看,修改,创建功能

          反射的作用

          1. 在运行时检查类型信息
          • 获取任何一个对象的类型(Class)
          • 获取类的所有成员(字段/属性、方法、构造函数)
          • 判断一个对象是否属于某个特定类,或者实现了某个接口
          1. 在运行时操作类、对象和成员
          • 动态创建对象:即使编译时不知道类的名字,也可以在运行时通过字符串形式的类名来创建实例
          • 动态调用方法:通过方法的名字(字符串形式)来调用也给对象的方法
          • 动态获取和设置字段值:访问甚至修改一个对象的字段(包括私有字段,取决于语言的安全策略)
          1. 晚期绑定(Late Binding)
          • 调用COM组件或那些在编译时无法直接引用的程序集中的方法。不需要在项目中提前”Add Reference“,可以在运行时动态加载程序集并与之交互

          反射的优缺点

          优点

          • 极高的灵活性和动态性:这是其最大价值。它使得编写通用框架、插件系统、序列化/反序列化工具、对象关系映射器(ORM)成为可能
          • 赋能框架:像ASP.NET Core(依赖注入、MVC路由)、Entity Framework Core(将数据库表映射到实体类)、xUnit(发现并运行测试方法)等而核心.NET技术都重度依赖反射来实现

          缺点:

          • 性能开销:反射操作比直接编译代码调用要慢几个数量级。因为所有的检查、方法查找和成员访问都是在运行时进行的,JIT编译器无法对其进行优化。应避免在性能关键的循环或路径中使用
          • 安全性:反射可以绕过访问修饰符,这可能破坏封装性,带来安全风险
          • 代码可维护性:大量使用反射的代码难以阅读、理解和调试,因为很多行为要到运行时才能确定
          • 脆弱性:如果通过字符串名称查找类型或成员,一旦名称拼写错误或成员不存在,错误将在运行时才抛出,而不是在编译时

          反射使用场景

          优先考虑编译时安全性和性能,仅在别无他法或收益远大于成本时,才在严格限制下使用反射\

          1. 构建基础设施、框架和工具 编写通用工具或辅助方法
            一个深度克隆(Deep Clone)工具方法。需要遍历一个对象的所有字段(包括私有字段),来创建一个完美的副本。这种方法通常会在项目的基础工具库中写一次,然后被广泛复用
            时刻问自己:“我是在编写像框架一样的通用基础设施吗?”如果答案是否定的,那么你很可能不应该使用反射。日常业务中,99.9%的情况都有更好的、不依赖反射的解决方案

          2. 与外部系统或动态配置集成 当你的程序需要根据外部配置(如配置文件、数据库中的设置)来动态决定行为时
            一个简单的插件系统。在配置文件中写上插件类的名称"MyApp.Plugins.EmailNotifier",主程序通过Type.GetType()创建并加载它。虽然用了反射,但它被隔离在程序启动的初始化阶段,不会影响核心性能
            反射操作比直接代码调用慢几个数量级。在性能关键的路径上使用反射是自杀行为

          3. 使用重度依赖反射的成熟库 很多时候,已经在“间接地”使用反射了,只是库帮你处理了所有复杂的细节,并做了性能优化

          4. 绝对尊重封装 仅仅因为能访问私有字段,不意味着应该这么做。修改私有成员是最后的手段,通常只用于测试、序列化等框架场景。滥用它会创建极其脆弱和难以维护的代码,因为代码与类的内部实现细节紧密耦合,一旦类的私有成员改变,代码就会立刻崩溃

          5. 安全风险 如果正在处理敏感数据(如密码、令牌),反射的存在意味着任何能运行代码的组件都可能提取这些数据。在设计安全敏感的系统时,必须考虑到这一点

          C#反射核心

          核心命名空间及其下的类

          • System
            • System.Type:一个类型的Type对象包含所有该类型的元数据信息
          • System.Reflection
            • Assembly, Module, MemberInfo, MethodInfo, PropertyInfo, FieldInfo, ConstructorInfo, ParameterInfo, BindingFlags
          • System.Reflection.Emit:动态生成/IL代码
            • AssemblyBuilder, ModuleBuilder, TypeBuilder, MethodBuilder, ILGenerator, DynamicMethod
          • System.Reflection.Metadata:高级、只读元数据分析,用于直接读PE/元数据(通常用于工具/静态分析)
          • System.Runtime.Serialization/System.Reflection.TypeExtension(若干辅助API)

          System.Type/TypeInfo

          背景

          TypeInfoType功能的扩展,是Type的增强视图,它提供了更丰富、更符合现代.NET开发模式的反射API,尤其是在.NET Core和现代应用模型中
          在传统的、完整的.NET Framework中,反射的核心一直是System.Type类。所有关于类型的信息都通过它来获取
          当微软开始开发.NET Core(一个跨平台、模块化、高性能的.NET版本)时,他们需要一个策略来让平台更轻量级。一个核心思想是按需付费:你的应用程序不应该被迫加载一个庞大的完整程序集,而是只加载它实际需要的部分
          传统的System.Type位于mscorlib.dll(后来是System.Runtime.dll)中,它是一个非常庞大和全面的类型。为了支持这种模块化,反射API被拆分和重新设计
          System.Type是一个装工具的大工具箱。TypeInfo就像是把这个工具箱打开,把里面的工具(方法、属性、基类等信息)都拿出来,整齐地摆在你面前,让你看得更清楚,用起来也更符合“按需取用”的原则
          **System.Type vs TypeInfo

          特性System.Type(传统方式)TypeInfo(现代方式)
          所在程序集System.RuntimeSystem.Reflection
          设计哲学“拉”(Pull)。你向Type请求信息(例如,调用.GetMethods(),它才会去加载并返回“展”(Expose)API。类型的信息已经作为属性展示在TypeInfo对象上了
          成员访问通过方法获取数组成员(如GetMethods(), GetProperties()通过属性获取集合成员(如DeclaredMethods, DeclaredProperties),这些集合是预计算好的
          延迟加载√ 当你调用GetMethod()时,它才去计算并返回结果概念上更倾向于提前计算。TypeInfo对象本身就承载着这些信息集合
          主要使用场景传统的.NET Framework应用.NET Core+、.NET 5+、UWP、Xamarin等现代应用模型。特别是依赖于Microsoft.Extensions.DependencInjection的应用(如ASP.NET Core)

          System.Type

          获取
          1. typeof运算符:当你在编译时就知道类型时
          Type stringType = typeof(string);
          Type myClassType = typeof(MyClass);
          
          1. Object.GetType()实例方法:当你有一个对象实例时
          Person person = new Person();
          Type personType = person.GetType(); // 获取 person 实例的运行时类型
          
          1. Type.GetType()静态方法:当你只有类型的完全限定名称字符串时(最动态的方式)
          // 格式:“命名空间.类名,程序集名”
          Type type = Type.GetType("System.String, mscorlib"); // 获取string的类型
          Type type2 = Type.GetType("MyNamespace.MyClass, MyAssembly");
          
          内容

          Type类包含了海量的信息,以下分类列出最重要和最常用的部分

          1. 类型标识信息(Identity Information) 这些属性告诉你这个类型“是谁”
          • Name:类型的名称(例如:"String", "List1")
          • FullName:类型的完全限定名,包括命名空间(例如:"System.String", System.Collections.Generic.List1")
          • Namespace:类型所属的命名空间(例如"System"
          • Assembly:获取声明该类型的Assembly。非常重要,告诉你类型来自哪个.dll或.exe
          • IsClass, InInterface, IsEnum, IsValueType, IsPrimitive(如int, bool):一系列布尔属性,用于判断类型的种类
          • BaseType:获取该类型直接继承的父类的Type。这是实现继承树遍历的基础
          1. 成员discovery(发现成员) 这是反射最核心的功能之一:获取类型的所有成员。这些方法通常返回XXXInfo类型的数组
          • GetMethods():返回所有公共方法的MethodInfo[]
          • GetProperties():返回所有公共属性的PropertyInfo[]
          • GetFields():返回所有公共字段的FieldInfo[]
          • GetEvents():返回所有公共事件的EventInfo[]
          • GetConstructors():返回所有公共构造函数的ConstructorInfo[]
          • GetMembers():返回所有公共成员的MemberInfo[](是上述所有成员的基类)

          以上方法通常有重载版本,接收BindingFlags枚举参数,让你能精确控制搜索范围(例如,查找私有成员、静态成员等)

          1. 泛型信息(Generic Information) 对于泛型类型,Type提供了专门的处理
          • IsGenericType:判断是否是泛型类型(如List<T>
          • GetGenericTypeDefinition():获取泛型类型的未绑定版本的类型。例如,对于List<string>,调用子方法会返回List<T>Type
          • GetGenericArguments():获取泛型类型参数或泛型方法类型参数的Type数组。例如,对于Dictionary<string, int>,这会返回[typeof(string), typeof(int)]
          1. 接口实现(Interface Implementation)
          • GetInterfaces():获取该类型实现的所有接口的Type[]
          1. 创建实例与获取特定成员
          • Activator.CreateInstance(Type):虽然这不是Type的实例方法,但它是使用Type对象最常见的方式之一:动态创建该类型的实例
          object myInstance = Activator.CreateInstance(myType);
          
          • GetMethod(string name), GetProperty(string name), GetField(string name)等:通过名称获取特定的成员,返回对应的MethodInfo, PropertyInfo等。这比遍历数组高效
          1. 特性(Attributes)信息
          • GetCustomAttributes():获取应用于此类型的所有自定义特性(Attribute)。这是实现“约定由于配置”模式的基础
          var attributes = myType.GetCustomAttributes(typeof(ObsoleteAttribute), false);
          if (attribute.Length > 0) Console.WriteLine("This class is obsolete!")
          
          1. 判断类型特征属性(Isxxx类型本质
          属性说明示例
          IsClass是否为类(不含接口、值类型)typeof(string).IsClass → true
          IsInterface是否为接口typeof(IDisposable).IsInterface → true
          IsValueType是否为值类型(struct/enum)typeof(int).IsValueType → true
          IsEnum是否为枚举typeof(DayOfWeek).IsEnum → true
          IsPrimitive是否为基元类型(int, bool, char 等)typeof(int).IsPrimitive → true
          IsArray是否为数组typeof(int[]).IsArray → true
          IsPointer是否为指针类型typeof(int*).IsPointer → true
          IsByRef是否为 ref 传递类型typeof(int).MakeByRefType().IsByRef → true
          IsSubclassOf(Type)是否为某类型的子类typeof(List<int>).IsSubclassOf(typeof(object)) → true

          类修饰符相关

          属性说明示例
          IsAbstract是否为抽象类/抽象方法typeof(Stream).IsAbstract → true
          IsSealed是否为密封类(sealed)typeof(string).IsSealed → true
          IsPublic是否为公共类型typeof(string).IsPublic → true
          IsNotPublic是否为非公共类型内部类等
          IsVisible是否对外可见(公共并且程序集可见性允许)

          嵌套类可见性
          当类型时嵌套类才有意义

          属性说明
          IsNestedPublic公共嵌套类
          IsNestedPrivate私有嵌套类
          IsNestedFamilyprotected 嵌套类
          IsNestedAssemblyinternal 嵌套类
          IsNestedFamANDAssemprotected internal 且要在同一程序集中
          IsNestedFamORAssemprotected internal 或程序集可见

          泛型相关

          属性说明示例
          IsGenericType是否为泛型类型typeof(List<int>).IsGenericType → true
          IsGenericTypeDefinition是否为开放泛型定义typeof(List<>).IsGenericTypeDefinition → true
          IsConstructedGenericType是否为已构造泛型(有具体类型参数)typeof(List<int>).IsConstructedGenericType → true
          ContainsGenericParameters是否还包含未绑定的泛型参数typeof(Dictionary<,>).ContainsGenericParameters → true
          IsGenericParameter是否为泛型参数本身T 这样的泛型占位符时才为 true

          高级/特殊情况

          属性说明
          IsCOMObject是否为 COM 对象类型
          IsMarshalByRef是否为按引用封送(Remoting 相关)
          IsSecurityCritical / IsSecuritySafeCritical / IsSecurityTransparent与安全模型相关
          IsSerializable是否可序列化
          IsImport是否使用 ComImportAttribute 标记

          TypeInfo

          获取
          using System.Reflection;
          
          Type type = typeof(string); // 先获取普通Type对象
          
          TypeInfo typeInfo = type.GetTypeInfo(); // 通过Type对象获取增强视图TypeInfo
          
          Console.WriteLine(typeInfo.FullName);
          foreach (var method in typeInfo.DeclaredMethods)
              Console.WriteLine(method.Name);
          

          这是一种优雅的向后兼容策略。现有的代码仍然使用Type,完全不受影响。而新的、面向现代平台的代码可以轻松地切花电脑更强大的TypeInfoAPI

          内容

          TypeInfo不仅包含了Type的所有功能(通过其基类),还添加了更符合逻辑的分组和查询方式

          1. 声明成员(Declared Members) 返回直接在该类型上声明的成员,而不是从基类继承来的
          • DeclaredMethods
          • DeclaredProperties
          • DeclaredFields
          • DeclaredConstructors
          • DeclaredEvents
          • DeclaredNestedTypes
          1. 更清晰的继承链分析
          • BaseType属性仍然存在(来自Type),直接获取基类
          • ImplementedInterfaces:获取该类型实现的所有接口的集合(IEnumerable<Types>),这比Type.GetInterfaces()返回数组更现代,更适合LINQ操作
          1. 泛型信息的扩展
          • GenericTypeParameters:对于泛型类型定义(如List<T>),获取其泛型参数列表(TType数组)。这与Type.GetGenericArguments()行为类似,但集成在TypeInfo的API中
          • GenericTypeArguments:对于构造的泛型类型(如List<string>),获取其类型实参(stringType数组)
          • IsGenericTypeDefinition:判断是否是泛型类型定义
          1. Attributes信息
          • GetCustomAttributes():虽然Type也有此方法,但TypeInfo上的版本更好地集成在了新的反射模型中,用于检查应用于该类型的特性
          1. 程序集和模块信息
          • Assembly:获取声明该类型的程序集(Assembly
          • Module:获取声明该类型的模块(Module)。模块是程序集内部的子结构
          核心价值
          1. 声明视图:提供了DeclaredXxx属性,让你能专注于类型自身声明的成员,过滤掉继承的“噪音”,这是它相对于直接使用TypeGetMethods()等方法的巨大优势
          2. LINQ友好:其属性大多返回IEnumerable<T>集合,可以无缝于LINQ查询集成,使得对类型元数据的查询、过滤和投影操作变得非常简洁和强大
          3. 现代API设计:它代表了.NET反射和API的现代化方向,更侧重于暴露数据而不是通过方法调用获取数据,这更符合.NET Core的轻量级和可组合性理念
          4. 功能完备:因为它继承自Type,所以可以在任何需要Type对象的地方使用TypeInfo,同时还能享受这些新增功能的强大

          在现代.NET开发中(.NET5+),当需要进行复杂的类型内省时,TypeInfo应该是首先工具,而传统的Type方法则更适合简单的、一次性的反射操作

          System.Reflection

          System.Reflection是.NET Runtime的一个内省(introspection)和交互工具包,提供了程序在运行时检查、发现和操作程序集、模块、类型及其成员的所有能力

          这个命名空间包含的类的能力:

          1. 加载和分析程序集(Assembly
          2. 检查和遍历类型结构(Type, TypeInfo
          3. 操作类型成员:方法、属性、字段、构造函数、事件等
          4. 处理泛型和自定义特性

          关键类和结构

          Assembly

          程序集的代表。反射的起点之一。可以用它来加载程序集、获取其中定义的所有类型、获取程序集信息(名称、版本、文化等) Assembly.LoadFrom("MyLibrary.dll"):从文件路径加载程序集 Assembly.GetExecutingAssembly():获取当前正在执行的代码所在的程序集 assembly.GetTypes():获取程序集中定义的所有类型

          Module

          代表程序集内的一个模块(.netmodule)。程序集由一个或多个模块组成,但在日常开发中很少使用,通常一个程序集就是一个模块

          Type & TypeInfo

          类型的代表。这是反射最核心的类,包含了关于类型的所有元数据。TypeInfoType的扩展视图,在现代.NET中提供更丰富的API

          MemberInfo派生类

          这些类提供了对类型成员的具体操作能力,它们都继承自MemberInfo

          • ConstructorInfo:提供了关于构造函数的信息。用于动态创建对象实例
            • constructor.Invoke(new object[] { arg1, arg2 }):调用构造函数创建对象
          • MethodInfo:提供关于方法的信息。用于动态调用方法
            • method.Invoke(obj, new object[] { param1 }):在对象obj上调用此方法
          • PropertyInfo:提供关于属性的信息。用于动态获取或设置属性值
            • prop.GetValue(obj)/prop.SetValue(obj, value)
          • FieldInfo:提供关于字段(成员变量)的信息。用于动态读取或写入字段值,甚至可以访问private字段
            • field.GetValue(obj)/field.SetValue(obj, value)
          • EventInfo:提供关于事件的信息。用于动态添加或移除事件处理程序
            • eventInfo.AddEventHandler(obj, handler)
          • ParameterInfo:提供关于方法参数的信息
          其他
          • BindingFlags:用于精确控制如何搜索和返回成员。例如,可以指定要搜索public/non-publicstatic/instance成员
            • BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly
          • CustomAttributeExtensions:提供扩展方法(如GetCustomAttribute())来检索应用于类型或成员的自定义特性
          • TargetException, TargetParamterCountException等:反射操作失败时抛出的特定异常
          • IsDefined:判断某个特性是否应用在类型或成员上
            • 所在类型
              • MemberInfo.IsDefined(Type attributeType, bool inherit)
              • ParameterInfo.IsDefined(Type attributeType, bool inherit)
              • Module.IsDefined(Type attributeType, bool inherit)
              • Assembly.IsDefined(Type attributeType, bool inherit)
            • 返回值:bool

          System.Reflection.Emit

          System.Reflection的作用是检查和分析已有代码元数据,System.Reflection.Emit就是让在运行时动态地创建和生成全新的代码
          它提供了在内存中或磁盘上动态构建新Assembly、Module、Type and Method的能力,甚至可以直接生成IL指令

          核心思想:运行时代码生成

          不是在VS里写C#代码然后编译成.dll,而是用C#代码编写一个程序,这个程序本身会像一个编译器一样,在运行时输出另一个.dll或在内存中创建一个新的类型。这就是Reflection.Emit所做的事

          关键类

          这个命名空间包含一整套用于按层次结构构建程序集的类

          AssemblyBuilder, ModuleBuilder
          • AssemblyBuilder:代表一个正在构建中的动态程序集。这是所有动态代码生成的起点。可以选择将其保存到磁盘上的一个.dll.exe文件,或者仅仅在内存中创建它
            • 通过AppDomain.DefineDynamicAssembly(旧版)或AssemblyBuilder.DefineDynamicAssembly(.NET Core+)创建
          • ModuleBuilder:代表动态程序集内的一个模块。程序集由一个或多个模块组成。必须在模块内定义类型
            • 通过AssemblyBuilder.DefineDynamicModule创建
          TypeBuilder, EnumBuilder
          • TypeBuilder:用于在模块内定义新的类、接口、结构体。可以指定其名称、可见性、父类、实现的接口等
            • 通过ModuleBuilder.DefineType创建
          • EnumBuilder:用于定义枚举类型。是TypeBuilder的一种特化形式
          MethodBuilder, ConstructorBuilder, FieldBuilder, PropertyBuilder, EventBuilder

          这些类用于为动态类型添加成员你

          • MethodBuilder:用于在动态类型上定义方法。可以指定方法名、参数、返回类型、可见性等
          • ConstructorBuilder:用于定义构造函数(.ctor)和静态构造函数(.cctor
          • FieldBuilder:用于定义字段(成员变量)
          • PropertyBuilder:用于定义属性(包括getset访问器)
          • EventBuilder:用于定义事件
          ILGenerator

          编写实际代码,这是最底层的,当使用MethodBuilderConstructorBuilder定义了方法的签名后,需要一个ILGenerator来填充方法的实际逻辑

          • ILGenerator这个类提供了发出(Emit)IL操作码的方法。IL是.NET的底层指令集
            • 通过MethodBuilder.GetILGenerator()获取
            • 使用其方法如Emit(OpCode)EmitWriteLine来生成指令
            • 试用期方法来定义标签(DefineLabel)、声明局部变量(DeclareLocal),实现流程控制(如if判断、循环)

          常见的IL指令示例

          • OpCodes.Ldarg_0:加载第0个参数(对于实例方法,通常是this
          • OpCodes.Ldstr:加载一个字符串
          • OpCodes.Call:调用一个方法
          • OpCodes.Ret:从方法返回
          • OpCodes.Newobj:调用构造函数创建新对象
          • OpCodes.Stfld:将值存储到对象的字段中

          主要用途和应用场景

          1. 动态代理(AOP - Aspect-Oriented Programming)
          • 框架如Castle DynamicProxy就是基于Reflection.Emit构建的。它们能在运行时生成一个继承自目标类或实现目标接口的代理类,并在调用实际方法前后插入代码(如日志、事务、缓存等)
          1. ORM和序列化器的动态实现
          • 为了极致性能,ORM(如Dapper的高阶用法)可以动态生成一个专门用于映射特定实体类和数据行的代码,避免使用反射带来的开销
          1. 编译器或脚本引擎
          • 动态语言(如IronPython, IronRuby)的.NET实现使用它来将脚本代码编译成.NET程序集
          • 你自己的领域特定语言(DSL)解释器
          1. 动态生成高性能代码:
          • 对于某些复杂的、基于配置的算法,可以动态生成最优化的IL代码,这比通过反射调用或解释配置要快得多

          总结

          System.Reflection.Emit是一个极其强大但也非常复杂的底层API。它让你从”代码的消费者”变成了“代码的生产者”

          • 优点:无与伦比的灵活性和性能潜力
          • 缺点:
            • 难:需要理解.NET类型系统、CLR内存模型和IL指令
            • 难以调试:生成的IL代码很难调试和诊断
            • 脆弱:生成的代码如果出错,通常会在运行时导致可怕的InvalidProgramException

          因此,绝大多数普通开发者不会直接使用它,而是使用基于它构建的高级库。它的主要用户是框架、库和编译器开发者

          System.Reflection.Metadata

          System.Refelction.Metadata是一个更现代、更轻量级、更高性能的用于读取.NET程序集元数据的API。它提供了对ECMA-335(.NET程序集标准格式)的低级访问

          核心思想:低级、只读、高性能

          System.Reflection不同,System.Reflection.Metadata

          1. 只读(Read-Only):它只用于读取元数据,不能用于动态创建或修改类型(那是System.Reflection.Emit的工作)
          2. 低级(Low-Level):它不提供高层的抽象(如TypeMethodInfo对象),而是提供对元数据表、对、Blob的直接访问。需要自己组装出需要的信息
          3. 高性能(High-Performance):因为它避免了创建大对象(如为每个方法创建一个MethodInfo实例)和复杂的逻辑,所以它的速度极快,内存占用极低
          4. 无负载(No Load):它不会将程序集假爱到当前的AppDomainAssemblyLoadContext中。它只是直接解析程序集的字节流。这意味着它更安全,不会导致程序集冲突或锁定文件

          关键概念和核心类

          要理解这个命名空间,需要先了解ECMA-335元数据格式的几个核心概念

          • 元数据表(Metadata Tables):程序集元数据被组织成一系列的表,例如TypeDef表(类型定义)、MethodDef表(方法定义)、FieldDef表(字段定义)等。每张表都由许多行组成
          • 句柄(Handles):这是System.Refelction.Metadata的核心标识符。它是对元数据表中某一行的一个轻量级、高效的引用(类似于数据库中的行ID)。例如TypeDefinitionHandle, MethodDefinitionHandle。使用句柄来从阅读器中获取具体数据
          • Blob & Heap:字符串、签名、常量值等数据不是直接存储在表里,而是存储在特殊的堆(Heap)区域中,表里只能存储一个指向堆的偏移量(Blob Pointer)
          MetadataReader

          这是整个API的核心和入口点。通过它来访问程序集的所有元数据

          • 如何创建:通过一个byte[](程序集的字节)或Stream(指向程序集文件的流)来创建PEReader,然后再从PEReader获取MetadataReader
          • 功能:它提供了属性和方法来访问所有的元数据表、解析句柄、读取字符串等
          XXXDefinitionXXXDefinitionHandle

          对于每种元数据元素,通常有一对类:

          • XXXDefinition:包含该元素的实际数据(如TypeDefinition包含类型的特性、名称、基类等)
          • XXXDefinitionHandle:用于获取对应XXXDefinition的句柄

          例如:

          • TypeDefinition & TypeDefinitionHandle
          • MethodDefinition & MethodDefinitionHandle
          • FieldDefinition & FieldDefinitionHandle
          • AssemblyDefinition & AssemblyDefinitionHandle
          其他重要类型
          • EntityHandle:所有具体句柄(如TypeDefinitionHandle)的基类
          • BlobReader:用于读取Blob堆中的二进制数据(例如方法签名)
          • StringReader:用于从字符串堆中读取字符串

          如果用传统的System.Reflection,代码会简单得多:Assembly.LoadFrom("MyLibrary.dll").GetTypes()。但代价是加载了整个程序集,消耗了更多资源和时间

          主要用途和应用场景

          1. 编译器和分析器(Roslyn)
          • .NET编译器平台(Roslyn)本身大量使用此API来快速分析代码和元数据,而无需加载所有程序集
          1. 高级代码分析工具
          • 需要快速扫描大量程序集以生成依赖关系图、查找特定模式、检查代码规范的工具。因为它的低内存开销,可以同时处理成百上千个程序集
          1. IDE功能
          • Visual Studio 或 VS Code中的"Go to Definition"、“Find All References”、智能感知(IntelliSense)等功能需要在后台快速读取元数据,这个API是理想选择
          1. 序列化器/映射器的高级优化
          • 类似于Reflection.Emit的用途,但这里是用于分析而非生成。一个库可以在启动时使用System.Reflection.Metadata快速分析所有需要序列化的类型结构,然后为它们动态生成(通过Emit)最优化的序列化代码
          1. 安全的元数据检查
          • 当需要检查一个程序集的信息(版本、公开类型等),但又绝对不想把它加载到应用程序域中执行时(处于安全或隔离考虑),这是完美工具

          原类型如何避免被反射

          1. SecurityCriticalSecuritySafeCritical特性(在部分信任环境中) 在旧的.NET Framework代码访问安全(CAS)模型中,可以将代码标记为安全关键的,这会在部分信任的沙箱环境中阻止非特权代码通过反射进行访问。不过,这套模型在现代.NET中已经被认为过时且很少使用

          2. 运行时检查并拒绝 理论上,可以在属性的setter或方法里检查是谁在调用它。.NET提供了StackTraceCallingAssembly等类来检查调用堆栈

          public string SecretPassword
          {
            set
            {
              var stackTrace = new StackTrace();
              var callingMethod = stacckTrace.GetFrame(1)?.GetMethod();
          
              // 如果调用者是通过反色和来的,可以拒绝
              if (callingMethod?.Name == "Invoke" && callingMethod.DeclaringType?.Name == "RuntimeMethodInfo")
              {
                throw new InvalidOperationException("Direct assignment only! No reflection allowed!");
              }
              _secretPassword = value;
            }
          }
          

          但是这种方法非常脆弱、有性能开销,而且很容易被绕过(例如,反射调用底层私有字段_secretPassword就直接绕过了这个属性setter),因此极不推荐

          反射在Unity中的使用

          性能优化

          反射与委托结合

          反射与泛型结合

          反射与LINQ结合

          最佳实践

          底层原理

          底层理解

          MethodInfo.Invoke, Property.SetValue, Property.GetValue

          反射链路