【C#反射】Assembly
Assembly属性的应用
//获取当前执行代码的程序集 Assembly assem = Assembly.GetExecutingAssembly(); Console.WriteLine($"程序集全名:{assem.FullName}" ); Console.WriteLine($"程序集的版本:{assem.GetName().Version}" ); Console.WriteLine($"程序集位置:{assem.Location}" ); Console.WriteLine($"程序集入口:{assem.EntryPoint}"); Console.WriteLine($"获取用于加载程序集的主机上下文:{assem.HostContext}"); Console.WriteLine($"是否保留在可回收的 AssemblyLoadContext 中:{assem.IsCollectible}"); Console.WriteLine($"CLR 版本的文件夹名:{assem.ImageRuntimeVersion}"); Console.WriteLine($"当前程序集是否在当前进程中动态生成的:{assem.IsDynamic}"); Console.WriteLine($"当前程序集是否以完全信任方式加载:{assem.IsFullyTrusted}"); Console.WriteLine($"当前程序集清单的模块:{assem.ManifestModule}"); Console.WriteLine($"获取包含此程序集中模块的集合:{assem.Modules}"); Console.WriteLine($"程序集被加载到只反射上下文而不是执行上下文中:{assem.ReflectionOnly}"); Console.WriteLine($"CLR 对此程序集强制执行的安全规则集:{assem.SecurityRuleSet}"); Type[] types = assem.GetTypes(); Console.WriteLine("程序集下包含的类型:"); foreach (var item in types) { Console.WriteLine($"类:{item.Name}"); } //输出结果: //程序集全名:ConsoleApp3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null //程序集的版本:1.0.0.0 //程序集位置:C:\Users\HP\source\repos\ConsoleApp3\bin\Debug\net5.0\ConsoleApp3.dll //程序集入口:Void Main(System.String[]) //获取用于加载程序集的主机上下文:0 //是否保留在可回收的 AssemblyLoadContext 中:False //CLR 版本的文件夹名:v4.0.30319 //当前程序集是否在当前进程中动态生成的:False //当前程序集是否以完全信任方式加载:True //当前程序集清单的模块:ConsoleApp3.dll //获取包含此程序集中模块的集合:System.Reflection.RuntimeModule[] //程序集被加载到只反射上下文而不是执行上下文中:False //CLR 对此程序集强制执行的安全规则集:None //程序集下包含的类型: //类:Program //类:Person //类:Employee
Assembly方法的应用
使用System.Reflection.Assembly类动态加载程序集(.dll)
Assembly类可以获得程序集的信息,也可以动态的加载程序集,以及在程序集中查找类型信息,并创建该类型的实例。 使用Assembly类可以降低程序集之间的耦合,有利于软件结构的合理化。
Assembly.Load()方法,Assembly.LoadFrom()方法,Assembly.LoadFile()方法的区别!
LoadFrom()、LoadFile()这俩方法会锁住文件,导致程序运行期间无法对load过的程序集文件进行更名/删除/覆盖等等操作,考虑用Assembly.Load()文件字节组替代:Load()其他方法加载文件也会锁住文件。
以下程序集不能共享 JIT 编译代码:使用Assembly 类的 LoadFrom 方法加载到“加载源”上下文中的程序集,或者使用 Load 方法的重载(指定字节数组 Load(byte[] rawAssembly、Load(byte[] rawAssembly, byte[]? rawSymbolStore))从图像加载的程序集。也就是说用这些方法加载的程序集只能给当期程序集使用,不能共享给其他程序集使用 详细请看:应用程序域和程序集
(1)Assembly.Load() 加载程序集
在使用这个函数之前要添加对程序集的引用,以.net core 5.0为例。
点击确定。添加成功后,查看项目的配置文件ConsoleApp4.csproj
此时会多出一条配置信息 如下图所示,很多时候load()无法加载函数都是因为配置文件未添加引用程序集信息。也可以手动添加该信息 引用程序集。
以上配置完成后就可以在.net core中正常使用Load函数加载程序集了。.net framework框架下也要添加程序集的引用
// Assembly.Load 函数的使用 Byte[] syd = System.IO.File.ReadAllBytes("Education.dll");//在指定的路径下获取程序集,不需要添加引用,默认当前当前程序集的目录下获取 Assembly assemblyLoadByte = Assembly.Load(syd); //必须把程序集添加为引用 才能正常使用该函数功能 Assembly assemblyLoadFullname= Assembly.Load("Education, Version = 1.1.0.0, Culture = neutral, PublicKeyToken = be45ec9bf8b890a7"); Assembly assemblyLoadPartialname = Assembly.Load("Education");
(2)Assembly.LoadFrom()加载 Assembly对象
LoadFrom()方法可以从指定文件中加载程序集,通过查找程序集的AssemblyRef元数据表,得知所有引用和需要的程序集,然后在内部调用Load()方法进行加载。
加载进的程序集实例存储在 AssemblyLoadContext.Default这个context中,和程序的依赖项存储在一起。如果希望存储在单独的context中 ,查看AssemblyLoadContext详细用法
例如:
Assembly.LoadFrom(@"C:\ABC\Test.dll");
Assembly ass = Assembly.LoadFrom("ClassLibrary831.dll");//文件在当前程序目录下
这个方法从指定的路径来加载程序集,实际上这个方法被调用的时候,CLR会打开这个文件,获取其中的程序集版本,语言文化,公钥标记等信息,把他们传递给 Load方法,接着,Load方法采用上面的策略来查找程序集。如果找到了程序集,会和LoadFrom方法中指定的路径做比较,如果路径相同,该程序集会被认为是应用程序的一部分,如果路径不同或Load方法没有找到程序集,那该程序集只是被作为一个“数据文件”来加载,不会被认为是应用程序的一部分。 这就是在第1点中提到的Load方法比LoadFrom方法的执行效率高的原因。另外,由于可能把程序集作为“数据文件”来加载,所以使用 LoadFrom从不同路径加载相同程序集的时候会导致重复加载。当然这个方法会加载此程序集引用的其他程序集。
(3)Assembly.LoadFile()加载 Assembly对象
这个方法是从指定的文件来加载程序集,和上面方法的不同之处是这个方法不会加载此程序集引用的其他程序集!
结论:一般大家应该优先选择Load方法来加载程序集,如果遇到需要使用LoadFrom方法的时候,最好改变设计而用Load方法来代替!
(4) ,LoadWithParitalName方法
对于,LoadWithParitalName方法,推荐大家最好不要使用它,因为程序无法确定最终要去加载哪个程序集的版本,所以我们这里只是简单的介绍一下它的工作原理:你可以传递一个程序集标识给它,包括程序集名称,至于其他信息是可选的(区域信息,公有密钥等),该方法执行时,会首先检查应用程序中配置文件的qualifyAssembly节点,如果存在,则把该部分名称的程序集替换成完全的程序集标识,如果不存在,则使用程序集名称先到应用程序根目录下查找,然后是私有目录,没有找到的话,就到程序集全局缓存中查找。简单过程如下:
应用程序根目录 -> 应用程序私有目录 -> 程序集全局缓存.
Assembly.LoadFile 与 Assembly.LoadFrom的区别
1、Assembly.LoadFile只载入相应的dll文件,比如Assembly.LoadFile("abc.dll"),则载入abc.dll,假如abc.dll中引用了def.dll的话,def.dll并不会被载入。
Assembly.LoadFrom则不一样,它会载入dll文件及其引用的其他dll,比如上面的例子,def.dll也会被载入。
2、用Assembly.LoadFrom载入一个Assembly时,会先检查前面是否已经载入过相同名字的Assembly,比如abc.dll有两个版本(版本1在目录1下,版本2放在目录2下),程序一开始时载入了版本1,当使用Assembly.LoadFrom("2\\abc.dll")载入版本2时,不能载入,而是返回版本1。Assembly.LoadFile的话则不会做这样的检查,比如上面的例子换成Assembly.LoadFile的话,则能正确载入版本2。
LoadFile:加载指定路径上的程序集文件的内容。LoadFrom: 根据程序集的文件名加载程序集文件的内容。
Assembly.CreateInstance()创建实例
assembly.CreateInstance已经获知程序集所有元数据,所以只要指定命名空间.类名 就可以生成实例了。Activator.CreateInstance未获得元数据,所以需要typeof函数先获取元数据类型信息,然后才能实例化。 点击查看Activator.CreateInstance实例化
// Assembly.Load 函数的使用 Byte[] syd = System.IO.File.ReadAllBytes("Education.dll");//在指定的路径下获取程序集,不需要添加引用,默认当前当前程序集的目录下获取 Assembly assemblyLoadByte = Assembly.Load(syd); //必须把程序集添加为引用 采用正常使用该函数功能 Assembly assemblyLoadFullname= Assembly.Load("Education, Version = 1.1.0.0, Culture = neutral, PublicKeyToken = be45ec9bf8b890a7"); Assembly assemblyLoadPartialname = Assembly.Load("Education"); //Assembly.CreateInstance根据空间名.类型名创建实例,已经获得程序集所有的元数据,所以可以用这种放射创建 object person = assemblyLoadByte.CreateInstance("People.Person"); object stu = assemblyLoadByte.CreateInstance("Education.Student"); Type studentType = assemblyLoadByte.GetType("Education.Student");//名空间+类名 才能挣钱获取类型
Assembly.GetExecutingAssembly();
Assembly assem = Assembly.GetExecutingAssembly(); //获取当前执行代码的程序集
Assembly其他应用
通过Assembly获取程序集中类
Type t = ass.GetType("ClassLibrary831.NewClass"); //参数必须是类的全名
通过Assembly获取程序集中所有的类
Type[] t = ass.GetTypes(); //通过程序集的名称反射 Assembly ass = Assembly.Load("ClassLibrary831"); Type t = ass.GetType("ClassLibrary831.NewClass"); object o = Activator.CreateInstance(t, "grayworm", "http://hi.baidu.com/grayworm"); MethodInfo mi = t.GetMethod("show"); mi.Invoke(o, null); //通过DLL文件全名反射其中的所有类型 Assembly assembly = Assembly.LoadFrom("xxx.dll的路径"); Type[] aa = a.GetTypes(); foreach(Type t in aa) { if(t.FullName == "a.b.c") { object o = Activator.CreateInstance(t); } }