【C#进阶系列】23 程序集加载和反射
2016-04-21 22:43
316 查看
程序集加载
程序集加载,CLR使用System.Reflection.Assembly.Load静态方法,当然这个方法我们自己也可以显式调用。
还有一个Assembly.LoadFrom方法加载指定路径名的程序集,实际上其内部是先通过AssemblyName.GetAssemblyName获取AssemblyName对象,然后调用Assembly.Load方法。
此时load方法会在各个位置(前面03章讲过)查找程序集,如果已经加载了此程序集就返回已加载的程序集,如果没有加载就去加载找到的程序集,如果没有找到,就加载路径所给的那个程序集。(所以很清楚了解到不一定会加载所指定的那个程序集,而可能是另一个。在这里如果每次生成强命名程序集时更新版本号,才会使LoadFrom方法的行为符合预期)
LoadFrom方法允许传递一个Url作为实参,CLR会下载文件,把它安装到用户的下载缓存中,再从那儿加载文件。
ReflectionOnlyLoadFrom函数也可以加载程序集,且禁止程序集中的任何代码执行。
使用反射构建动态可扩展应用程序
既然加载了程序集,那么就应该要有办法去使用程序集中定义的类,这种办法就是反射。
利用System.Reflection命名空间中包含的类型,可以写代码来反射元数据表,为所加载的程序集中所包含的元数据提供对象模型。
反射一些例子:
首先先建立一个用于反射的程序集,代码如下:
然后生成了一个叫HelloWorld.dll的文件,然后开始玩反射
反射的性能
反射是相当强大的机制,但是也有其缺点:
反射造成编译时无法保证类型安全性,因为它是在运行时才依靠字符串来对类进行实例化等操作。
反射的速度很慢,因为是在运行时靠字符串去标识成员,发现它们,使用它们。整个过程中都是用字符串来搜索。
设计支持加载项的应用程序
构建可扩展应用程序时,一般使用接口而不是基类,因为接口允许加载项开发人员选择自己的基类。
为宿主接口类的方法定义参数和返回类时,尝试使用MSCorLib.dll定义的接口和类型。因为CLR只加载一个MSCorLib.dl,所以不会出现类型版本不匹配的情况,且有助于减少应用程序对内存的需求。
反射与类型的成员
System.Reflection.MemberInfo封装了所有类型成员都通用的一组属性。它的一些派生类如MethodInfo则封装了与特定类型成员相关的更多属性。
直接上代码简单易懂:
对于FieldInfo(字段)和PropertyInfo(属性)可以用GetValue和SetValue来获取和设置实例的值,
对于MethodInfo(方法)和ConstructorInfo(构造器)则可以用Invoke来调用,
对于EventInfo(事件)可以用AddEventHandler和RemoveHandler来增加事件回调函数和减少回调函数。
上述方法其实很麻烦,如果用dynamic方法那么就会和一般的写程序一样简单了。
程序集加载,CLR使用System.Reflection.Assembly.Load静态方法,当然这个方法我们自己也可以显式调用。
还有一个Assembly.LoadFrom方法加载指定路径名的程序集,实际上其内部是先通过AssemblyName.GetAssemblyName获取AssemblyName对象,然后调用Assembly.Load方法。
此时load方法会在各个位置(前面03章讲过)查找程序集,如果已经加载了此程序集就返回已加载的程序集,如果没有加载就去加载找到的程序集,如果没有找到,就加载路径所给的那个程序集。(所以很清楚了解到不一定会加载所指定的那个程序集,而可能是另一个。在这里如果每次生成强命名程序集时更新版本号,才会使LoadFrom方法的行为符合预期)
LoadFrom方法允许传递一个Url作为实参,CLR会下载文件,把它安装到用户的下载缓存中,再从那儿加载文件。
ReflectionOnlyLoadFrom函数也可以加载程序集,且禁止程序集中的任何代码执行。
使用反射构建动态可扩展应用程序
既然加载了程序集,那么就应该要有办法去使用程序集中定义的类,这种办法就是反射。
利用System.Reflection命名空间中包含的类型,可以写代码来反射元数据表,为所加载的程序集中所包含的元数据提供对象模型。
反射一些例子:
首先先建立一个用于反射的程序集,代码如下:
namespace HelloWorld { public class Man { public string _name; public Man(String name) { this._name = name; } public void ShowName() { Console.WriteLine(this._name); } } } namespace HelloWorld { public class Troy:Man { private string _jobName; public Troy(string name,string jobName):base(name) { this._jobName = jobName; } public void ShowJobName() { Console.WriteLine(this._jobName); } } }
然后生成了一个叫HelloWorld.dll的文件,然后开始玩反射
//首先加载程序集,获取程序集对象 Assembly myAssembly=Assembly.LoadFrom("D:\\HelloWorld.dll"); //玩程序集中定义的公共类型 foreach (Type type in myAssembly.ExportedTypes) { //打印类型全名 Console.WriteLine("类型全名:"+type.FullName); Console.WriteLine(type.FullName + "的基类:" + type.BaseType.FullName); //判定类型是否为String(当然这是不可能的,因为只有Man和Troy) if (type == typeof(String)) { Console.WriteLine("有个String类型"); } //Type对象是轻量型的类型引用,更全面的信息在TypeInfo对象(获取TypeInfo对象会强迫CLR确保已加载类型的定义程序集,从而对类型进行解析。(代价高昂)), //如下转换 TypeInfo typeInfo = IntrospectionExtensions.GetTypeInfo(type); //也可以反着转 Type tmpType = typeInfo.AsType(); //泛型类型的Type Type openType = typeof(Dictionary<,>);//开放类型 Type closedType= openType.MakeGenericType(typeof(int), type);//闭合类型 //实例化 Object obj= Activator.CreateInstance(closedType); Console.WriteLine(obj.GetType()); }
反射的性能
反射是相当强大的机制,但是也有其缺点:
反射造成编译时无法保证类型安全性,因为它是在运行时才依靠字符串来对类进行实例化等操作。
反射的速度很慢,因为是在运行时靠字符串去标识成员,发现它们,使用它们。整个过程中都是用字符串来搜索。
设计支持加载项的应用程序
构建可扩展应用程序时,一般使用接口而不是基类,因为接口允许加载项开发人员选择自己的基类。
为宿主接口类的方法定义参数和返回类时,尝试使用MSCorLib.dll定义的接口和类型。因为CLR只加载一个MSCorLib.dl,所以不会出现类型版本不匹配的情况,且有助于减少应用程序对内存的需求。
反射与类型的成员
System.Reflection.MemberInfo封装了所有类型成员都通用的一组属性。它的一些派生类如MethodInfo则封装了与特定类型成员相关的更多属性。
直接上代码简单易懂:
class Program { static void Main(string[] args) { Type type = typeof(Troy); Object obj = Activator.CreateInstance(type); MethodInfo[] arrMethod= type.GetMethods(); foreach (var methodInfo in arrMethod) { if (methodInfo.GetParameters().Length == 0) { methodInfo.Invoke(obj, null); } } Console.Read(); } } public class Troy{ public string name; public Troy() { name = "Troy"; } public void Show() { Console.WriteLine(name); } }
对于FieldInfo(字段)和PropertyInfo(属性)可以用GetValue和SetValue来获取和设置实例的值,
对于MethodInfo(方法)和ConstructorInfo(构造器)则可以用Invoke来调用,
对于EventInfo(事件)可以用AddEventHandler和RemoveHandler来增加事件回调函数和减少回调函数。
上述方法其实很麻烦,如果用dynamic方法那么就会和一般的写程序一样简单了。
相关文章推荐
- 图解C# 调用Win32 API 示例程序
- C#中String和string有什么区别
- C#基础之--线程、任务和同步:一、异步委托
- C#入门经典(第6版)
- C# structures 0011
- C#—异常应用(正则表达式)
- 计算阶乘-当输入的数据是带小数时引发异常
- C#—异常类
- c#重写TabControl控件实现关闭按钮的方法
- 在静态类中定义一个泛型方法查找数组元素
- PerformanceCounter 基本介绍以及示例方法
- 《C#入门经典v6》读书笔记5 WCF&WWF
- C# Substring 和 String.join 0010
- 进度条的使用
- 终于会用c#中的delegate(委托)和event(事件)了
- 【转载】继承(C#)
- C#控制台实现52张扑克牌的分法
- 4.20学习笔记c# OpenFileDialog
- C# 中引用类型 和值类型
- 【转载】C# Graphics类详解