程序集
什么是程序集?
- 1.程序集(assembly)是一个及一个以上托管模块,以及一些资源文件的逻辑组合。
- 2.程序集是组件复用,以及实施安全策略和版本策略的最小单位。
- 3.程序集是包含一个或者多个类型定义文件和资源文件的集合。在程序集包含的所有文件中,有一个文件用于保存清单。(清单是元数据部分中一组数据表的集合,其中包含了程序集中一部分文件的名称,描述了程序集的版本,语言文化,发布者,共有导出类型,以及组成该程序集的所有文件)。
- 4、在编译应用程序中,所创建的CIL代码存储在一个程序集中,程序集包括可执行的应用程序文件(.exe扩展名文件)和其他应用程序使用的库(.dll扩展名文件)。
- 简单的说在.NET生成的dll和exe都是程序集,但是c++生成的就不是了。程序集包含资源文件,类型元数据(描述在代码中定义的每一类型和成员,二进制形式)、IL代码(这些都被装在exe或dll中),每个程序集都有自己的名称、版本等信息。这些信息可以通过AssemblyInfo.cs文件来自己定义。
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的常规信息通过以下
// 特性集控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("AssemblyDemo")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AssemblyDemo")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 使此程序集中的类型
// 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
// 则将该类型上的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("e7da9959-0c97-444c-aa40-6d9bbf728068")]
// 程序集的版本信息由下面四个值组成:
//
// 主版本
// 次版本
// 内部版本号
// 修订号
//
// 可以指定所有这些值,也可以使用“内部版本号”和“修订号”的默认值,
// 方法是按如下所示使用“*”:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
AssemblyInfo.cs
程序集的优点:
- 解决版本控制问题,程序只需要应用必要的程序集,减少了编码量(例如log4net.dll),程序的尺寸
- 解决dll冲突(Windows历史上著名的 dll地狱)
- 以在程序集中封装一些代码,提供必要的接口,供引用该程序集的项目使用
程序集常用的方法
Assembly.Load()方法,Assembly.LoadFrom()方法,Assembly.LoadFile()方法的区别!
1,Assembly.Load()
这个方法通过程序集的长名称(包括程序集名,版本信息,语言文化,公钥标记)来加载程序集的,会加载此程序集引用的其他程序集,一般情况下都应该优先使用 这个方法,他的执行效率比LoadFrom要高很多,而且不会造成重复加载的问题(原因在第2点上说明)
使用这个方法的时候, CLR会应用一定的策略来查找程序集,实际上CLR按如下的顺序来定位程序集:
⑴如果程序集有强名称,在首先在全局程序集缓(GAC)中查找程序集。
⑵如果程序集的强名称没有正确指定或GAC中找不到,那么通过配置文件中的元素指定的URL来查找
⑶如果没有指定强名称或是在GAC中找不到,CLR会探测特定的文件夹:
假设你的应用程序目录是C:AppDir,元素中的privatePath指定了一个路径Path1,你要定位的程序集是AssemblyName.dll则CLR将按照如下顺序定位程序集
C:AppDirAssemblyName.dll
C:AppDirAssemblyNameAssemblyName.dll
C:AppDirPath1AssemblyName.dll
C:AppDirPath1AssemblyNameAssemblyName.dll
如果以上方法不能找到程序集,会发生编译错误,如果是动态加载程序集,会在运行时抛出异常!
2,Assembly.LoadFrom()
这个方法从指定的路径来加载程序集,实际上这个方法被调用的时候,CLR会打开这个文件,获取其中的程序集版本,语言文化,公钥标记等信息,把他们传递给 Load方法,接着,Load方法采用上面的策略来查找程序集。如果找到了程序集,会和LoadFrom方法中指定的路径做比较,如果路径相同,该程序集 会被认为是应用程序的一部分,如果路径不同或Load方法没有找到程序集,那该程序集只是被作为一个“数据文件”来加载,不会被认为是应用程序的一部分。 这就是在第1点中提到的Load方法比LoadFrom方法的执行效率高的原因。另外,由于可能把程序集作为“数据文件”来加载,所以使用 LoadFrom从不同路径加载相同程序集的时候会导致重复加载。当然这个方法会加载此程序集引用的其他程序集。
3,Assembly.LoadFile()
这个方法是从指定的文件来加载程序集,和上面方法的不同之处是这个方法不会加载此程序集引用的其他程序集!
结论:一般大家应该优先选择Load方法来加载程序集,如果遇到需要使用LoadFrom方法的时候,最好改变设计而用Load方法来代替!
另: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(“2abc.dll”)载入版本2时,不能载入,而是返回版本1。Assembly.LoadFile的话则不会做这样的检查,比如上面的例子换成Assembly.LoadFile的话,则能正确载入版本2。
LoadFile:加载指定路径上的程序集文件的内容。LoadFrom: 根据程序集的文件名加载程序集文件的内容。
区别:
LoadFile 方法用来来加载和检查具有相同标识但位于不同路径中的程序集.但不会加载程序的依赖项。
LoadFrom 不能用于加载标识相同但路径不同的程序集。
简而言之:如果动态引用其他厂家的dll,可以用LoadFrom,因为厂家的dll还以引用其他的dll,如果自己写的小dll,没有依赖项,则用LoadFile,但是这样很少用。最新版的只支持LoadFrom了。
反射
反射就是动态获取程序集中的元数据(提供程序集的类型信息)的功能。也就是动态获取程序集中的元数据来操作类型的。比如咱们使用的vs中的智能提示,就是通过反射获取类的方法、属性的。程序集包含模块,而模块包含类型,类型又包含成员。 反射则提供了封装程序集、模块和类型的对象。 您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。 然后,可以调用类型的方法或访问其字段和属性。
表示类型声明:类类型、接口类型、数组类型、值类型、枚举类型、类型参数、泛型类型定义,以及开放或封闭构造的泛型类型。
可以通过以下两种方式获得Type:
1.通过类获得Type: Type t=typeof(Person);
Assembly中对type的类型的获取
调用Assembly的GetExportedTypes方法可以得到Assembly中定义的所有的public类型。
调用Assembly的GetTypes()方法可以得到Assembly中定义的所有的类型。
调用Assembly的GetType(name)方法可以得到Assembly中定义的全名为name的类型信息。如: Type type = assembly.GetType( ” MyAssembly.Person ” );
动态创建对像
Activator.CreateInstance(Type t)会动态调用类的无参构造函数创建一个对象,返回值就是创建的对象,如果类没有无参构造函数就会报错。
GetConstructor(参数列表);//这个是找到带参数的构造函数。例如: object o = Activator.CreateInstance(type, ” wolf ” , 22 , ” 未知 ” ); // 实例化
Type函数介绍
属性:
•type.Assembly:获取type所在的程序集对象
•type.FullName:获取type对象对应的类的全名称
•type.Name: 获取type对象对应类的 名称
•type.IsArray: 判断type是否为一个数组类
•type.IsEnum: 判断type是否为一个枚举类
方法:
•type.IsAssignableFrom(Type i):判断type是否实现了接口i
•type.IsSubclassOf(Type father):判断type是否继承了father
•type.IsInstanceOfType(objecto):判断o是否为type类的实例
•type.GetFiled(“gender”):获取type中名为gender的字段对象
•type.GetMethod(“SayHi”):获取type中名为SayHi的方法对象
•type.GetProperty(“Age”):获取type中名为Age的属性对象
//动态加载一个程序集 Assembly assembly = Assembly.LoadFile(@"C: 2TestDll.dll"); //2.获取刚刚加载的程序集中的所有的类型 //assembly.GetType() 等价于 typeof(Assembly),不能获取某个程序集中国你的所有类型那个 //GetTypes()获取了所有的类型 Type[] types = assembly.GetTypes(); ////只获取那些public的类型 Type[] types = assembly.GetExportedTypes(); //加入程序集中有多个类,只获取Person类的Type //GetType()方法有重载,选择第二个重载,参数表示是要获取的类型的“完全限定名称”,即:命名空间.类名 //这里拿到了Type,其实就等价于typeof(Person)或者是:p.GetType(); Type personType = assembly.GetType("_02TestDll.Person"); //获取所有的方法:personType.GetMethods(); ////调用一个无参数,无返回值的方法 MethodInfo method = personType.GetMethod("SayHi"); Console.WriteLine(method.Name); //通过反射来创建一个Person类型的对象{其实就是通过Person的Type来创建一个Person对象} object objPerson = Activator.CreateInstance(personType); //调用这个方法 method.Invoke(objPerson, null); //调用带参数,带返回值的方法 //1>找到对应的方法 MethodInfo method = personType.GetMethod("Add"); object obj = Activator.CreateInstance(personType); //2>调用 object result = method.Invoke(obj, new object[] { 102, 203 }); //调用重载的方法 //1>找到对应的方法 MethodInfo method = personType.GetMethod("Add", new Type[] { typeof(int), typeof(int), typeof(int) }); object obj = Activator.CreateInstance(personType); //2>调用 int r = (int)method.Invoke(obj, new object[] { 1, 2, 3 }); #region 通过反射获取类的属性,并赋值 //1.获取Name属性 PropertyInfo property = personType.GetProperty("Name"); object obj = Activator.CreateInstance(personType); //2.为属性赋值 property.SetValue(obj, "闫刘盘", null); //3.获取属性 string name = property.GetValue(obj, null).ToString(); #endregion //#region 手动查找类型的构造函数,并且调用该构造函数来创建类型的对象 //查找到了对应的构造函数,但是还没有调用 ConstructorInfo ctor = personType.GetConstructor(new Type[] { typeof(string), typeof(int), typeof(string) }); //开始调用构造函数 object obj = ctor.Invoke(new object[] { "hpp", 16, "hpp@yahoo.com" }); bool IsAssignableFrom(Type c):(直译:是否可以从c赋值)判断当前的类型的变量是不是可以接受c类型变量的赋值。表示可以将Student类型赋值给Person类型,因为Student类型继承自Person类 bool IsInstanceOfType(object o):判断对象o是否是当前类的实例(当前类可以是o的类、父类、接口) bool IsSubclassOf(Type c):判断当前类是否是类c的子类。只验证类与类之间的父子类关系,接口不包含。 IsAbstract 判断是否为抽象的,含接口