朝花夕拾-.NET反射
什么是反射,反射(Reflection)是.NET中的重要机制,微软提供的一个帮助类库,命名空间System.Reflection,主要类型System.Type。通过反射,可以在运行时获得.NET中每一个类型(包括类、结构、委托、接口和枚举等)的成员,包 括方法、属性、事件,以及构造函数等。常见的反射: MVC AOP IOC ORM
本文主要是记录在回味学习过程中接收到的一些反射的使用,主要包含了:反射的几种方式、反射使用,反射的扩展封装(程序可配置)、通过反射选择不同的构造函数创建对象、反射的调用
反射扩展,访问私有,破坏单例模式(可以调用私有函数,每次都被创建新的对象)、反射调用泛型、反射使用属性字段等实例。
因会设计到反射的使用,所以先贴项目结构和代码。此处只是简单的项目的项目结构,用于更好的查看效果。
实例项目结构
1、项目结构
1、DB.Interface
1 namespace DB.Interface 2 { 3 public interface IDBHelper 4 { 5 void Query(); 6 } 7 }
2、DB.SqlServer
1 using System 2 /// <summary> 3 /// 反射调用泛型 4 /// </summary> 5 namespace DB.SqlServer 6 { 7 /// <summary> 8 /// 类中泛型方法 9 /// </summary> 10 public class GenericMethod 11 { 12 public void show<T, W, X>(T t, W w, X x) 13 { 14 Console.WriteLine("t.Type={0},w.Type={1},x.Type={2}", t.GetType().Name, w.GetType().Name, x.GetType().Name); 15 } 16 } 17 18 /// <summary> 19 /// 泛型类中泛型方法 20 /// </summary> 21 /// <typeparam name="T"></typeparam> 22 /// <typeparam name="W"></typeparam> 23 /// <typeparam name="X"></typeparam> 24 public class GenericTest<T, W, X> 25 { 26 public void show(T t, W w, X x) 27 { 28 Console.WriteLine("t.Type={0},w.Type={1},x.Type={2}", t.GetType().Name, w.GetType().Name, x.GetType().Name); 29 } 30 } 31 32 33 34 public class GenericDouble<T> 35 { 36 public void show<W, X>(T t, W w, X x) 37 { 38 Console.WriteLine("t.Type={0},w.Type={1},x.Type={2}", t.GetType().Name, w.GetType().Name, x.GetType().Name); 39 } 40 } 41 }
1 using System; 2 3 namespace DB.SqlServer 4 { 5 public class RefectionTest 6 { 7 /// <summary> 8 /// 无参 9 /// </summary> 10 public RefectionTest() { 11 Console.WriteLine("这里是{0}无参构造函数",this.GetType()); 12 } 13 14 /// <summary> 15 /// 带参string 16 /// </summary> 17 public RefectionTest(string name) 18 { 19 Console.WriteLine("这里是{0}有参构造函数string", this.GetType()); 20 } 21 22 /// <summary> 23 /// 带参int 24 /// </summary> 25 public RefectionTest(int id) 26 { 27 Console.WriteLine("这里是{0}有参构造函数int", this.GetType()); 28 } 29 30 /// <summary> 31 /// 无参 带参 32 /// </summary> 33 public void RefectionTestShow() { 34 Console.WriteLine("这里是{0}的Show1无参",this.GetType()); 35 } 36 37 public void RefectionTestShow1(string name) 38 { 39 Console.WriteLine("这里是{0}的Show1带参string", this.GetType()); 40 } 41 42 public void RefectionTestShow2(int id,string name) 43 { 44 Console.WriteLine("这里是{0}的Show1带参int", this.GetType()); 45 } 46 47 /// <summary> 48 /// 重载开始 49 /// </summary> 50 /// <param name="id"></param> 51 public void RefectionTestShow3(int id) 52 { 53 Console.WriteLine("这里是{0}的RefectionTestShow3带参int", this.GetType()); 54 } 55 public void RefectionTestShow3(int id, string name) 56 { 57 Console.WriteLine("这里是{0}的Show1带参int,string", this.GetType()); 58 } 59 60 /// <summary> 61 /// 私有方法 62 /// </summary> 63 /// <param name="id"></param> 64 private void RefectionTestShow4(int id) 65 { 66 Console.WriteLine("这里是{0}RefectionTestShow4私有", this.GetType()); 67 } 68 69 /// <summary> 70 /// 静态方法 71 /// </summary> 72 /// <param name="id"></param> 73 public static void RefectionTestShow5(int id) 74 { 75 Console.WriteLine("这里是{0}RefectionTestShow5",typeof(RefectionTest)); 76 } 77 } 78 }
1 using System; 2 3 namespace DB.SqlServer 4 { 5 /// <summary> 6 /// 单例 7 /// </summary> 8 public sealed class Singleton 9 { 10 private static Singleton _singleton = null; 11 private Singleton() 12 { 13 Console.WriteLine("Singleton被构造"); 14 } 15 16 static Singleton() 17 { 18 _singleton = new Singleton(); 19 } 20 21 public static Singleton GetInstance() 22 { 23 return _singleton; 24 } 25 } 26 }
1 using DB.Interface; 2 using Model; 3 using System; 4 using System.Collections.Generic; 5 using System.Configuration; 6 using System.Data.SqlClient; 7 using System.Linq; 8 using System.Text; 9 using System.Threading.Tasks; 10 11 namespace DB.SqlServer 12 { 13 public class SqlServerHelper : IDBHelper 14 { 15 private static string Customers = ""; 16 public SqlServerHelper() 17 { 18 Console.WriteLine("{0}被构造", this.GetType().Name); 19 } 20 public void Query() 21 { 22 Console.WriteLine("{0}.Query", this.GetType().Name); 23 } 24 25 /// <summary> 26 /// 数据库查询 27 /// </summary> 28 /// <param name="Id"></param> 29 /// <returns></returns> 30 public static Company GetCompanyById(int Id) 31 { 32 string sql = @"SELECT [Id] 33 ,[Name] 34 ,[CreateTime] 35 ,[CreatorId] 36 ,[LastModifierId] 37 ,[LastModiyTime] 38 FROM [Customers].[dbo].[Company] WHERE id=" + Id; 39 Type type = typeof(Company); 40 object oCompany = Activator.CreateInstance(type); 41 using (SqlConnection connection = new SqlConnection(Customers)) 42 { 43 SqlCommand sqlCommand = new SqlCommand(sql, connection); 44 connection.Open(); 45 SqlDataReader reader = sqlCommand.ExecuteReader(); 46 if (reader.Read()) 47 { 48 //foreach (var prop in type.GetProperties()) 49 //{ 50 // if (prop.Name.Equals("Id")) 51 // prop.SetValue(oCompany, reader["Id"]); 52 // if (prop.Name.Equals("Name")) 53 // prop.SetValue(oCompany, reader["Name"]); 54 //} 55 foreach (var prop in type.GetProperties()) 56 { 57 prop.SetValue(oCompany, reader[prop.Name]); 58 } 59 60 } 61 } 62 63 return (Company)oCompany; 64 } 65 66 67 public static T GetCompanyById<T>(int Id) where T : BaseModel//约束 68 { 69 70 Type type = typeof(T); 71 object oCompany = Activator.CreateInstance(type); 72 var propNames = type.GetProperties().Select(s => $"[{s.Name}]"); 73 string props = string.Join(",", propNames); 74 string sql = $"SELECT {props} FROM [{type.Name}] WHERE id={Id}"; 75 using (SqlConnection connection = new SqlConnection(Customers)) 76 { 77 SqlCommand sqlCommand = new SqlCommand(sql, connection); 78 connection.Open(); 79 SqlDataReader reader = sqlCommand.ExecuteReader(); 80 if (reader.Read()) 81 { 82 foreach (var prop in type.GetProperties()) 83 { 84 prop.SetValue(oCompany, reader[prop.Name]); 85 } 86 87 } 88 } 89 90 return (T)oCompany; 91 } 92 } 93 }
3、Model
1 namespace Model 2 { 3 public class BaseModel 4 { 5 public int Id { get; set; } 6 } 7 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Model 8 { 9 public class Company : BaseModel 10 { 11 public string Name{ get; set; } 12 13 public DateTime CreateTime { get; set; } 14 15 public int CreatorId { get; set; } 16 17 public int? LastModifierId { get; set; } 18 } 19 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Model 8 { 9 public class People 10 { 11 public People() { 12 Console.WriteLine("{0}被创建", this.GetType().FullName); 13 } 14 15 public int Id { get; set; } 16 public string Name { get; set; } 17 public string Description; 18 } 19 }
4、MyReflection_Net
1 using DB.Interface; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Reflection; 6 using System.Text; 7 using System.Threading.Tasks; 8 using System.Configuration; 9 10 namespace MyReflection_Net 11 { 12 public class SimlpFactory 13 { 14 private static string IDBSqlHelperConfig = ConfigurationManager.AppSettings["IDBSqlHelperConfig"];//配置 15 private static string typeName = IDBSqlHelperConfig.Split(',')[0];//类名称 16 private static string dllName = IDBSqlHelperConfig.Split(',')[1];//DLL名称 17 public static IDBHelper CreateInstentce() 18 { 19 //1、动态加载 20 Assembly assembly = Assembly.Load(dllName); 21 Type type = assembly.GetType(typeName); 22 //3、创建对象 23 object oDBHelper = Activator.CreateInstance(type); 24 //4、类型转换 25 IDBHelper iDBHelper = oDBHelper as IDBHelper;//此方式类型如果不匹配就会返回一个NULL 26 return iDBHelper; 27 } 28 } 29 }
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <startup> 4 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> 5 </startup> 6 <appSettings> 7 <!--配置反射DLL,第一个指DLL中需要反射的类,第二个是DLL名称--> 8 <add key="IDBSqlHelperConfig" value="DB.SqlServer.SqlServerHelper,DB.SqlServer"/> 9 </appSettings> 10 <connectionStrings> 11 <!--数据库连接字符串配置--> 12 <add name="Customers" connectionString=""></add> 13 </connectionStrings> 14 </configuration>
反射的几种方式
1 Assembly assembly = Assembly.Load("DB.SqlServer"); //DLL/EXE名称(默认当前目录查找) 2 Assembly assembly1 = Assembly.LoadFile(@"R:\MyReflection_Net\bin\Debug\DB.SqlServer.dll");//全名称/完整物理路径 3 Assembly assembly2 = Assembly.LoadFrom(@"R:\MyReflection_Net\bin\Debug\DB.SqlServer.dll");//带后缀(可以全名称,可以默认当前目录查找) 4 //输出 //获取对象 5 foreach (Type type in assembly.GetTypes()) 6 { 7 Console.WriteLine("assembly:" + type.Name); 8 foreach (MethodInfo method in type.GetMethods()) 9 { 10 Console.WriteLine("method" + method.Name); 11 } 12 }
反射的使用
1 //1、动态加载 2 Assembly assembly = Assembly.Load("DB.SqlServer"); //DLL/EXE名称(默认当前目录查找) 3 //2、获取类型 4 Type type = assembly.GetType("DB.SqlServer.SqlServerHelper");//命名空间+类名 5 //3、创建对象 6 object oDBHelper = Activator.CreateInstance(type); 7 //4、类型转换 8 //注:由于C#是强类型语言,所以不能直接调用方法,如:oDBHelper.Query(),会被编译器不认可,所以需要用到dynamic(动态类型,可以避开编译器检查,由于避开编译器检查,此方式会存在安全问题,导致无法使用) 9 //dynamic dDBHerper = Activator.CreateInstance(type); 10 // dDBHerper.Query(); 11 IDBHelper iDBHelper = oDBHelper as IDBHelper;//此方式类型如果不匹配就会返回一个NULL 12 //5、调用方法 13 iDBHelper.Query();
反射的封装调用
我们在这里可以对反射的调用进行封装,创建了SimlpFactory类,并在App.Config中对需要反射调用的DLL或EXE进行配置。这样做的好处是:
1、编写代码的时候工作量小
2、遇到业务变更或者新的业务时不需要重新编译和发布,通过配置可解决,同时也减轻了工作量
IDBHelper iDBherper = SimlpFactory.CreateInstentce();
iDBherper.Query();
通过反射选择不同的构造函数创建对象
1 //1、动态加载 2 Assembly assembly = Assembly.Load("DB.SqlServer"); //DLL/EXE名称(默认当前目录查找) 3 //2、获取类型 4 Type type = assembly.GetType("DB.SqlServer.RefectionTest");//命名空间+类名 5 //3、创建对象 6 object oDBHelper = Activator.CreateInstance(type); 7 object oDBHelper1 = Activator.CreateInstance(type, new object[] { "杰克" }); 8 object oDBHelper2 = Activator.CreateInstance(type, new object[] { "我是谁" }); 9 object oDBHelper3 = Activator.CreateInstance(type, new object[] { 123 });
反射不同方法、参数的调用
1 Assembly assembly = Assembly.Load("DB.SqlServer"); 2 Type type = assembly.GetType("DB.SqlServer.RefectionTest"); 3 object otest = Activator.CreateInstance(type); 4 //1、无参数 5 MethodInfo show = type.GetMethod("RefectionTestShow"); 6 show.Invoke(otest, new object[0]);//反射调用方法 7 //2、有参数(int) 8 MethodInfo show1 = type.GetMethod("RefectionTestShow1"); 9 show1.Invoke(otest, new object[] { "123" });//反射调用方法,参数类型需要和方法参数类型保持一致 10 //3、多参数 11 MethodInfo show2 = type.GetMethod("RefectionTestShow2", new Type[] { typeof(int), typeof(string) }); 12 show2.Invoke(otest, new object[] { 1, "123" });//多个参数 13 //4、重载 14 MethodInfo show3 = type.GetMethod("RefectionTestShow3", new Type[] { typeof(int), typeof(string) }); 15 show3.Invoke(otest, new object[] { 1, "123" });///多个参数 16 //5、调用私有方法 17 MethodInfo show4 = type.GetMethod("RefectionTestShow4", BindingFlags.NonPublic | BindingFlags.Instance); 18 show4.Invoke(otest, new object[] { 1 }); 19 //6、调用静态 20 MethodInfo show5 = type.GetMethod("RefectionTestShow5"); 21 show5.Invoke(otest, new object[] { 1 });//反射调用静态方法,对象的实例可以传入,也可不传入(null) 22 show5.Invoke(null, new object[] { 2 });
反射扩展,访问私有(可以调用私有函数,每次都被创建新的对象)
1 Singleton singleton1 = Singleton.GetInstance(); 2 Singleton singleton2 = Singleton.GetInstance(); 3 Console.WriteLine(singleton1.Equals(singleton2)); 4 Assembly assembly = Assembly.Load("DB.SqlServer"); 5 Type type = assembly.GetType("DB.SqlServer.Singleton"); 6 object oSingleton1 = Activator.CreateInstance(type, true);//参数true:访问私有的函数 7 object oSingleton2 = Activator.CreateInstance(type, true); 8 Console.WriteLine(oSingleton1.Equals(oSingleton2));
反射调用泛型 泛型编译后需要生成占位符如: `1 `2 ` 3
1 //原始方式 2 // GenericMethod genericMethod = new GenericMethod(); 3 // genericMethod.show<int, string, DateTime>(123,"",DateTime.Now); 4 //反射 5 //类中调用泛型方法 6 Assembly assembly = Assembly.Load("DB.SqlServer"); 7 Type type = assembly.GetType("DB.SqlServer.GenericMethod");//获取方法的时候不需要占位符 8 object oTest = Activator.CreateInstance(type); 9 MethodInfo genericMethod = type.GetMethod("show"); 10 MethodInfo genericMethod1 = genericMethod.MakeGenericMethod(new Type[] { typeof(int), typeof(string), typeof(DateTime) });//指定方法的具体参数 11 genericMethod1.Invoke(oTest, new object[] { 123, "GenericMethod", DateTime.Now }); 12 //泛型类中调用泛型方法 13 Assembly assembly1 = Assembly.Load("DB.SqlServer"); 14 Type typeGenericMethod = assembly.GetType("DB.SqlServer.GenericTest`3");//需要占位符 ,GenericTest<T, W, X>三个占位符所以是`3 15 Type typeGenericMethod1 = typeGenericMethod.MakeGenericType(new Type[] { typeof(int), typeof(string), typeof(DateTime) }); 16 object oGenericMethod = Activator.CreateInstance(typeGenericMethod1); 17 MethodInfo genericMethod1_1 = typeGenericMethod1.GetMethod("show"); 18 genericMethod1_1.Invoke(oGenericMethod, new object[] { 234, "GenericTest", DateTime.Now });
反射使用属性、字段
1 //八、反射使用属性字段 2 //1、获取类型 3 Type type = typeof(People); 4 //创建对象 5 object oPeople = Activator.CreateInstance(type); 6 //字段 7 foreach (PropertyInfo prop in type.GetProperties()) 8 { 9 if (prop.Name.Equals("Id")) 10 prop.SetValue(oPeople, 123); 11 if (prop.Name.Equals("Name")) 12 prop.SetValue(oPeople, "Name"); 13 Console.WriteLine($"name:" + prop.Name + "=" + prop.GetValue(oPeople)); 14 } 15 //属性 16 foreach (FieldInfo field in type.GetFields()) 17 { 18 if (field.Name.Equals("Description")) 19 field.SetValue(oPeople, "Description"); 20 Console.WriteLine($"name:" + field.Name + "=" + field.GetValue(oPeople)); 21 }
最后简单的对比一下我们在日常工作中使用反射和不使用反射的区别,此处用调用数据库的Helper做实例
1 public static Company GetCompanyById(int Id) 2 { 3 string sql = @"SELECT [Id] 4 ,[Name] 5 ,[CreateTime] 6 ,[CreatorId] 7 ,[LastModifierId] 8 ,[LastModiyTime] 9 FROM [Customers].[dbo].[Company] WHERE id=" + Id; 10 Type type = typeof(Company); 11 object oCompany = Activator.CreateInstance(type); 12 using (SqlConnection connection = new SqlConnection(Customers)) 13 { 14 SqlCommand sqlCommand = new SqlCommand(sql, connection); 15 connection.Open(); 16 SqlDataReader reader = sqlCommand.ExecuteReader(); 17 if (reader.Read()) 18 { 19 //此处是对比字段,可优化成第二种 20 //第一种 21 //foreach (var prop in type.GetProperties()) 22 //{ 23 // if (prop.Name.Equals("Id")) 24 // prop.SetValue(oCompany, reader["Id"]); 25 // if (prop.Name.Equals("Name")) 26 // prop.SetValue(oCompany, reader["Name"]); 27 //} 28 //第二种 29 foreach (var prop in type.GetProperties()) 30 { 31 prop.SetValue(oCompany, reader[prop.Name]); 32 } 33 34 } 35 } 36 37 return (Company)oCompany; 38 }
1 public static T GetCompanyById<T>(int Id) where T : BaseModel//约束 2 { 3 4 Type type = typeof(T); 5 object oCompany = Activator.CreateInstance(type); 6 var propNames = type.GetProperties().Select(s => $"[{s.Name}]"); 7 string props = string.Join(",", propNames); 8 string sql = $"SELECT {props} FROM [{type.Name}] WHERE id={Id}"; 9 using (SqlConnection connection = new SqlConnection(Customers)) 10 { 11 SqlCommand sqlCommand = new SqlCommand(sql, connection); 12 connection.Open(); 13 SqlDataReader reader = sqlCommand.ExecuteReader(); 14 if (reader.Read()) 15 { 16 foreach (var prop in type.GetProperties()) 17 { 18 prop.SetValue(oCompany, reader[prop.Name]); 19 } 20 21 } 22 } 23 24 return (T)oCompany; 25 }
总结
反射特点:
1、动态:减少对象之间的依赖,只需要指导类明、方法名,就可以达到自己调用的目的
2、可以突破特定的权限,可以做到普通方式无法做到的目的,如访问对象中的私有方法
3、编写比较困难,识别性不太好,编写和后期维护相对麻烦
4、由于反射回经过一系列的处理,会导致性能损耗相对差一些, 效率比较:1百万次差距 普通140ms 反射35000ms,可以通过缓存进行优化,但是依然比普通方式相差10倍以上,所以根据自己的需要来使用