C#教程之自己动手写映射第三节[反射]
一、什么是反射
MSND:反射提供了封装程序集、模块和类型的对象(Type 类型)。可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。如果代码中使用了属性,可以利用反射对它们进行访问。
实用概念:反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为反射。
反射的名称空间:System.Reflection,具体参考 http://msdn.microsoft.com/zh-cn/library/system.reflection.aspx
二、利用反射动态创建类的实例
前置条件:我们创建两个项目来展示反射的简单应用
- CSharp.Model:类库,Employee.cs,我们要反射的测试实体类。
- CSharp.Reflection:控制台应用程序项目,Program.cs为主程序类,App.config配置文件,配置我们要反射的程序集的名称,Constant.cs用于调用配置文件的常量类。
CSharp.Model.Employee代码如下:
1 /* 2 * 3 * 创建人:李林峰 4 * 5 * 时 间:2012-7-23 6 * 7 * 描 述:反射类的实例 8 * 9 */ 10 11 using System; 12 using System.Collections.Generic; 13 using System.Text; 14 15 namespace CSharp.Model 16 { 17 /// <summary> 18 /// 员工类 19 /// </summary> 20 public class Employee 21 { 22 public int ID { get; set; } //编号 23 public string Name { get; set; } //姓名 24 public string Password { get; set; } //密码 25 public string Department { get; set; } //部门 26 public string Position { get; set; } //职位 27 28 /// <summary> 29 /// 测试方法 30 /// </summary> 31 /// <returns></returns> 32 public string Method() 33 { 34 return this.ID.ToString(); 35 } 36 } 37 }
CSharp.Reflection的配置文件与常量类代码如下:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <appSettings> 4 <!--程序集名称--> 5 <add key="AssemblyName" value="CSharp.Model"></add> 6 </appSettings> 7 </configuration> 8 9 10 11 /* 12 * 13 * 创建人:李林峰 14 * 15 * 时 间:2012-7-23 16 * 17 * 描 述:常量类 18 * 19 */ 20 21 using System.Configuration; 22 23 namespace CSharp.Reflection 24 { 25 class Constant 26 { 27 /// <summary> 28 /// 程序集名称 29 /// </summary> 30 public static string ASSEMBLYNAME = ConfigurationManager.AppSettings["AssemblyName"]; 31 } 32 }
CSharp.Reflection的主程序类代码如下:
1 /* 2 * 3 * 创建人:李林峰 4 * 5 * 时 间:2012-7-23 6 * 7 * 描 述:应用程序入口 8 * 9 */ 10 11 using System.Reflection;//反射的名称空间 12 using CSharp.Model; //实体名称空间 13 14 namespace CSharp.Reflection 15 { 16 class Program 17 { 18 static void Main(string[] args) 19 { 20 Employee employee = (Employee)Assembly.Load(Constant.ASSEMBLYNAME).CreateInstance("CSharp.Model.Employee"); 21 employee.ID = 1; 22 employee.Name = "李林峰"; 23 employee.Department = "技术"; 24 employee.Position = "程序员"; 25 System.Console.WriteLine(employee.Name); 26 System.Console.WriteLine(employee.Department); 27 System.Console.WriteLine(employee.Position); 28 System.Console.WriteLine(employee.Method()); 29 30 System.Console.WriteLine("---------------------------------"); 31 32 Employee employeeNew = (Employee)Assembly.LoadFile(@"E:\公司内网\HZYT.Test\06 C#映射教程\ClassThree\CSharp.Reflection\bin\Debug\CSharp.Model.dll").CreateInstance("CSharp.Model.Employee"); 33 employee.ID = 2; 34 employeeNew.Name = "李林峰"; 35 employeeNew.Department = "技术"; 36 employeeNew.Position = "程序员"; 37 System.Console.WriteLine(employeeNew.Name); 38 System.Console.WriteLine(employeeNew.Department); 39 System.Console.WriteLine(employeeNew.Position); 40 System.Console.WriteLine(employee.Method()); 41 } 42 } 43 }
通过上面的示例可以看出,平时我们在创建对象的时候是通过 Employee employee = new Employee(); 来创建。而应用了反射后,我们通过方法System.Reflection.Assembly.Load("程序集名称").CreateInstance("类型名称");在运行时动态的创建类的实例。
上面的实例中我用了两个方法,主要是强调下反射中Load的是程序集,如上示例employee的实例创建,实际上反射的是已经被我们编译好的,存在于bin目录下的CSharp.Model.dll。而employeeNew我们用的方法是LoadFile,其实原理是一样的,只不过这个方法我们提供的是物理地址。
CreateInstance("类型名称")中的"类型名称"一定要为类的全名,即:"名称空间"+"类名"。
反射出来的为Object对象,我们在使用的时候要进行类型转换,如上例所示(Employee)Assembly.Load......
运行效果如下图所示:
三、利用反射动创建类
动态创建类:通过字符串描述类的结构,然后由.net编辑器编辑成类文件,如有必要还可以编辑成.dll的一个动态使用类的过程。
首先我们先进行类的描述,在很多框架中XML是类描述的主要文件类型,这里我们也不例外,为了简单起见我在本例中用了App.config文件来描述类。
前置条件:
- CSharp.Dynamic:控制台应用程序,Program.cs为主程序类,App.config用于描述类的结构,Constant.cs读取配置文件的常量类。
CSharp.Dynamic的配置文件与常量类代码如下:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <appSettings> 4 <add key="ClassDefined" value="namespace CSharp.Dynamic{ 5 public class Employee{ 6 public int ID { get; set; } 7 public string Name { get; set; } 8 public string Department { get; set; } 9 public string Position { get; set; } 10 public string Method(string parm){return parm+this.ID.ToString();} 11 }}"/> 12 </appSettings> 13 </configuration> 14 15 16 /* 17 * 18 * 创建人:李林峰 19 * 20 * 时 间:2012-7-23 21 * 22 * 描 述:常量类 23 * 24 */ 25 26 using System.Configuration; 27 28 namespace CSharp.Dynamic 29 { 30 class Constant 31 { 32 /// <summary> 33 /// 程序集名称 34 /// </summary> 35 public static string CLASSDEFINED = ConfigurationManager.AppSettings["ClassDefined"]; 36 } 37 }
CSharp.Dynamic的主程序类代码如下:
1 /* 2 * 3 * 创建人:李林峰 4 * 5 * 时 间:2012-7-23 6 * 7 * 描 述:利用反射动创建类实例 8 * 9 */ 10 11 using System; 12 using System.CodeDom.Compiler; 13 using Microsoft.CSharp; 14 using System.Reflection; 15 16 namespace CSharp.Dynamic 17 { 18 class Program 19 { 20 static void Main(string[] args) 21 { 22 //在App.Config中读取类的定义字符串 23 string ClassDefined = Constant.CLASSDEFINED; 24 //创建代码编译器 25 CSharpCodeProvider codeProvider = new CSharpCodeProvider(); 26 //设置编译参数 27 CompilerParameters paras = new CompilerParameters(); 28 //设置在内存中生成输出。 29 paras.GenerateInMemory = true; 30 //编译代码 31 CompilerResults result = codeProvider.CompileAssemblyFromSource(paras, ClassDefined); 32 //获取编译后的程序集 33 Assembly assembly = result.CompiledAssembly; 34 //获取反射出来的对象 35 Object dynamicClass = assembly.CreateInstance("CSharp.Dynamic.Employee"); 36 //获取员工实例的类型 37 Type employee = dynamicClass.GetType(); 38 //设置属性 39 employee.GetProperty("ID").SetValue(dynamicClass, 1, null); 40 employee.GetProperty("Name").SetValue(dynamicClass, "李林峰", null); 41 employee.GetProperty("Department").SetValue(dynamicClass, "技术部", null); 42 employee.GetProperty("Position").SetValue(dynamicClass, "程序员", null); 43 //读取属性 44 string Name = employee.GetProperty("Name").GetValue(dynamicClass, null).ToString(); 45 string Department = employee.GetProperty("Department").GetValue(dynamicClass, null).ToString(); 46 string Position = employee.GetProperty("Position").GetValue(dynamicClass, null).ToString(); 47 //执行方法 48 string ParmAndID = employee.GetMethod("Method").Invoke(dynamicClass, new object[] { "员工编号:" }).ToString(); 49 //输出 50 Console.WriteLine(Name); 51 Console.WriteLine(Department); 52 Console.WriteLine(Position); 53 Console.WriteLine(ParmAndID); 54 } 55 } 56 }
我们在App.config中定义了Employee类的结构,通过string ClassDefined = Constant.CLASSDEFINED;把字符串引用到程序中,并进行一系统的处理。
反射创建类的步骤如下:
- 用字符串定义类的结构。
- 创建代码编译器并编译。
- 获取编译后的程序集并反射出类的对象和类型。
- 属性、方法、事件等......赋值。
- 使用属性、方法、事件等......。
- 如果编译参数设置成内存输入[paras.GenerateInMemory = true;],则由.net 内存回收托管,否则在Temp[C:\Users\Administrator\AppData\Local\Temp]文件夹中产生临时文件"随机字符.dll",每运行一次该程序都将会产生一个新的DLL。
在上例中,除了导入了System.Reflection名称空间外还导入了System.CodeDom.Compiler与Microsoft.CSharp。
System.CodeDom.Compiler:支持编程语言的源代码的生成和编译进行管理。具体参考:http://msdn.microsoft.com/zh-cn/library/z6b99ydt
Microsoft.CSharp:编译和生成代码,名称空间下只有一个类CSharpCodeProvider。具体参考:http://msdn.microsoft.com/zh-cn/library/microsoft.csharp.aspx
设置属性:employee.GetProperty("ID").SetValue(dynamicClass, 1, null);
1 public virtual void SetValue( 2 Object obj, 3 Object value, 4 Object[] index 5 ) 6 7 参数 8 obj 9 类型:System.Object 10 将设置其属性值的对象。 11 12 value 13 类型:System.Object 14 此属性的新值。 15 16 index 17 类型:System.Object[] 18 索引化属性的可选索引值。 对于非索引化属性,该值应为 null。
读取属性:employee.GetProperty("Name").GetValue(dynamicClass, null);
1 public virtual Object GetValue( 2 Object obj, 3 Object[] index 4 ) 5 6 参数 7 obj 8 类型:System.Object 9 将返回其属性值的对象。 10 11 index 12 类型:System.Object[] 13 索引化属性的可选索引值。 对于非索引化属性,该值应为 null。 14 15 返回值 16 类型:System.Object 17 obj 参数指定的对象的属性值。
执行方法:employee.GetMethod("Method").Invoke(dynamicClass, new object[] { "员工编号:" });
1 public Object Invoke( 2 Object obj, 3 Object[] parameters 4 ) 5 6 参数 7 obj 8 类型:System.Object 9 实体对象。 10 11 Object[] parameters 12 类型:System.Object[] 13 参数数组,如果参数为空,该值应为 null。 14 15 返回值 16 类型:System.Object 17 obj 参数指定的对象的属性值。
上面的三个方法中的employee为类型,dynamicClass对象的实例。
运行效果如下图所示:
四、总结
反射在实际应用中还是比较广泛的,从设计模式的典型"抽象工厂模式"到我们平时应用到的"框架",都会用到反射,其原理大多是动态创建类,如:根据数据库的字段动态生成类,执行些方法等等。为了在介绍"C#教程之自己动手写映射"的课程,这里我只对反射常用的方法进行了讲解,如果感兴趣的话可查看MSND继续研究。
五、源码下载
六、版权
转载请注明出处:http://www.cnblogs.com/iamlilinfeng