反射--程序集

程序集

程序集详情,可以查看https://docs.microsoft.com/zh-cn/dotnet/standard/assembly/

程序集构成了 .NET 应用程序的部署、版本控制、重用、激活范围和安全权限的基本单元。采用可执行文件 (.exe) 或动态链接库文件 (.dll) 的形式,是 .NET 应用程序的构建基块 。是一个或多个类型定义文件及资源文件的集合。

程序集定义以下信息:

  • 公共语言运行时执行的代码。
  • 安全边界。
  • 类型边界。 每一类型的标识均包括该类型所驻留的程序集的名称。 在一个程序集的范围中加载的称为 MyType 的类型不同于在另一个程序集范围中加载的称为 MyType 的类型。
  • 引用范围边界。
  • 版本边界。
  • 部署单元。
  • 并行执行单元。

静态程序集可能由以下四个元素组成:其中只有程序集清单是必需的,但也需要类型或资源来向程序集提供任何有意义的功能。

  • 程序集清单,包含程序集元数据。
  • 类型元数据。
  • 实现这些类型的 Microsoft 中间语言 (MSIL) 代码。 它由编译器从一个或多个源代码文件生成。
  • 资源集。

其中C#源代码经过C#编译器,生成托管模块,最后编译器默认把生成的托管模块和资源文件合并成程序集。如果只有一个托管模块,且没有资源(或数据)文件的项目,程序集就是托管模块,生成过程中无需执行任何额外的步骤。
程序集是PE32文件或者64位Windows可移植执行体(PE32+:Portable Executable)文件,包括

  • PE32或PE32+头:
  • CLR头:包含了CLR版本,
  • 元数据:主要有两种表:分别是描述源代码中定义的类型和成员、引用的类型和成员
  • IL代码:编译器编译源代码时,生成的代码。运行时,CLR把IL代码编译成本机CPU指令。

代码执行过程:

  • JIT(just in time)编译器的职责:把方法的IL转换成本机(native)CPU指令
  • Console.WriteLine("Hello")的执行
    • 1、Main方法执行之前,CLR为Console类型分配一个内部结构,在这个内部结构中Console类型定义的每个方法都有一个对应的记录项
      每个记录项含有一个地址,根据此地址就可以找到此方法的实现
      对这个结构初始化时,CLR将每个记录项都设置成在CLR内部的一个未变档函数,成为JITCompiler
    • 2、首次调用,JITCompiler函数会被调用。负责将IL代码编译成本机CPU指令。将这个组件叫JIT编译器
      1、在负责实现类型的程序集的元数据中查找被调用的方法
      2、从元数据中获取被调用的方法的IL
      3、分配内存块
      4、将IL编译成本机CPU执行,然后将这些本地代码放到3中的内存
      5、在Type表中修改与方法对应的条目,使它指向3分配的内存块
      6、调整到内存块中的本机代码
      CPU指令存在动态内存中的
      JIT编译器会对本机代码进行优化

获取程序集:
获取不到抛出异常,而不是返回null值

//Assebmly静态方法获取程序集 
Assembly assembly = Assembly.Load("StudyConsole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
assembly = Assembly.Load("StudyConsole");
assembly = Assembly.Load(new AssemblyName("StudyConsole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") { });
            
Assembly assembly1 = Assembly.LoadFile(@"D:\1-办公\dnSpy\dnSpy\dnSpy.Console\obj\Debug\netcoreapp3.1\dnSpy.Console.dll");

Assembly assembly2 = Assembly.LoadFrom(@"D:\1-办公\dnSpy\dnSpy\dnSpy\bin\Debug\netcoreapp3.1\dnlib.dll");

//仅查看程序集,不能执行
Assembly assembly3 = Assembly.ReflectionOnlyLoad("");
Assembly assembly4 = Assembly.ReflectionOnlyLoadFrom("");

//获取当前执行的程序集
Assembly assembly8 = Assembly.GetExecutingAssembly();
//获取当前应用程序域所有加载的程序集
Assembly[] assembly6 = AppDomain.CurrentDomain.GetAssemblies();

//根据类型获取
Assembly assembly5 = Assembly.GetAssembly(typeof(System.String));
Assembly assembly7 = typeof(Program).Assembly;

//获取程序集的特性
var asstributes= assembly.CustomAttributes;
//获取引用的AssebmlyName
AssemblyName[] ass= assembly.GetReferencedAssemblies();

获取程序集的方法中,Load(string)方法,会先把string转换成Assebmly,在调用。和Load(AssebmlyName)的内部方法方式一致;LoadFrom方法和Load方法最终调用的方法一致,传入的地址最终转换成Assebmly类型。
(程序集名称的长格式包含其简单名称(如 System .dll 程序集的 "System")及其版本、区域性、公钥标记,还可以是其处理器体系结构。 它对应于程序集的 FullName 属性。)
Load方法会优先使用当前应用程序域中的程序集(猜测),因为当用LoadFile加载程序集之后,在加载同样的程序集不会报错。
Load方法初始化的是Assebmly的Name属性,LoadFrom是CodeBase;
其中LoadFile和Load方法实现方式不一致,CSDN(LoadFile 不会加载相应的引用程序集,但是 LoadFrom 会。 LoadFrom 不能用于加载具有相同标识但路径不同的程序集;它将只加载第一个此类程序集。)
ReflectionOnlyLoad、ReflectionOnlyLoadFrom最终的方法和Load方法一致,只是传入了一个区分的bool值。但是使用.NET CORE执行会报错。

程序集的属性截图:
反射--程序集
其中CodeBase和Location,都是文件地址;DefinedTypes是所有的类型,ExportedTypes是所有pulbli类。

上一篇:.NET MVC5+AUTOFAC实战


下一篇:Maven三种打包方式