C# 反射 Reflection Assembly
本章节带来的是反射,反射反射程序员的快乐。
一、什么叫反射
反射:是.net Framework提供给的一个方面metadata的帮助类,可以获取信息并且使用
反射的有点:动态
反射的缺点:1:稍微麻烦 2:能避开编译器的检查 3:性能损耗
二、反射如何使用:
具体和私用如下:
#region 反射的加载方式 ////获取当前路径下面的dl或者exe,不带后缀(MyReflection.exe 是编译后生成的exe执行文件),从Exe所在的路径进行查找 Assembly assembly = Assembly.Load(@"MyReflection"); //获取当前路径下面的dl或者exe Assembly assemblyFrom = Assembly.LoadFrom(@"D:\MyReflection\bin\Debug\MyReflection.exe"); //获取当前路径下面的dl或者exe Assembly assemblyFile = Assembly.LoadFile(@"D:\MyReflection\bin\Debug\MyReflection.exe"); foreach (var item in assembly.GetModules()) { //Modules当前的exe或者dll的名字(MyReflection.exe) Console.WriteLine(item.Name); } //当前所包含的实体类(IDBHelper,SqlServerHelper,Program) foreach (var item in assembly.GetTypes()) { foreach(var method in item.GetMethods()){ Console.WriteLine(method.Name); } Console.WriteLine(item.Name); } foreach (var item in assembly.GetCustomAttributes()) { Console.WriteLine(item.ToString()); } //获取到有多个构造函数 foreach (var ctor in type.GetConstructors()) { Console.WriteLine(ctor.GetParameters()); //获取构造函数里面的参数 foreach (var item in ctor.GetParameters()) { Console.WriteLine(item.ParameterType); } } #endregion
通过反射创建一个方法:
/// <summary> /// 反射得到一个对象 /// </summary> public class SimpleFactory { //读取配置文件AppSetting里面的key // <appSettings> // <add key = "DbConfig" value="MyReflection,MyReflection.MySqlServerHelper"/> //</appSettings> private static string ConfigStr = ConfigurationManager.AppSettings["DbConfig"]; private static string DllName = ConfigStr.Split(',')[0]; //命名空间 private static string TypeName = ConfigStr.Split(',')[1]; //类型要完整命名空间+类名 public static T CreateInstance<T>() { Assembly assembly = Assembly.Load(DllName);//加载dll Type type = assembly.GetType(TypeName);//获取类型 var objectInstance = Activator.CreateInstance(type); return (T)objectInstance; //要强制转换一下,因为牵涉到编译性语言和运行时语言 } }
调用的时候如下:
var mysqlServerHelper = SimpleFactory.CreateInstance<MySqlServerHelper>(); mysqlServerHelper.Query();
三、反射调用多构造函数,调用私有构造函数(破坏单例),调用泛型类
首先创建一个实体类,包含有参无参构造函数,然后有参无参的方法,如下:
/// <summary> /// sqlServer /// </summary> public class SqlServerHelper : IDBHelper { //private SqlServerHelper() //{ // Console.WriteLine("私有构造函数"); //} public SqlServerHelper() { Console.WriteLine("公有无参构造函数"); } public SqlServerHelper(int iParam) { Console.WriteLine($"int的构造函数--{iParam}"); } public SqlServerHelper(string sParam) { Console.WriteLine($"string的构造函数--{sParam}"); } public SqlServerHelper(int iParam, string sParam) { Console.WriteLine($"int和string的构造函数--int={iParam} ;string={sParam}"); } public void Show() { Console.WriteLine("Show"); } public void Show1() { Console.WriteLine("Show1的无参构造函数"); } public void Show1(int iParam) { Console.WriteLine($"Show1的int重载--{iParam}"); } public void Show1(int iParam, string sParam) { Console.WriteLine($"Show1两参数 iparam={iParam};sParam={sParam}"); } public static void Show5(string name) { Console.WriteLine($"静态方法---{name}"); } }
1:调用有参无参的public构造函数:
Assembly assembly = Assembly.Load("MyReflection"); //获取当前路径下面的dl或者exe,不带后缀 Type dbHelperType = assembly.GetType("MyReflection.SqlServerHelper"); //传完整名称获类型(命名空间+类名) //调用多个构造函数(有参,无参) var obSqlServerHelper = Activator.CreateInstance(dbHelperType); //无参的构造函数 Activator.CreateInstance(dbHelperType, new object[] { 11 }); //int的构造函数 Activator.CreateInstance(dbHelperType, new object[] { "testbywss" }); //调用string的构造函数 Activator.CreateInstance(dbHelperType, new object[] { 123, "testbywss" }); //调用string的构造函数
2:调用private构造函数:
//私有构造函数 Type singletonType = assembly.GetType("MyReflection.Singleton"); //传入完整名称获取类型(命名空间+类名) var object1 = Activator.CreateInstance(singletonType, true); //设置成true能调用私有/公布的构造函数,如果不设置则只能调用公有构造函数
3:普通方法,静态方法,重载方法的调用:
//普通方法 MethodInfo methodInfo = dbHelperType.GetMethod("Show"); //调用单个普通的实例方法 methodInfo.Invoke(obSqlServerHelper, null); //第一个参数:是应用对象,第二个参数:是方法需要的参数,如果没有则设置为null //静态方法调用 MethodInfo staticMethodInfo = dbHelperType.GetMethod("Show5"); //调用单个普通的实例方法 staticMethodInfo.Invoke(null, new object[] { "静态方法第一种调用方式" }); //第一个参数:是应用对象,如果是静态可以不用写;第二个参数:是方法需要的参数,如果没有则设置为null staticMethodInfo.Invoke(obSqlServerHelper, new object[] { "静态方法第二种调用方式" });
//重载方法调用 MethodInfo method2 = dbHelperType.GetMethod("Show1", new Type[] { }); //调用无参的方法 method2.Invoke(obSqlServerHelper, null); MethodInfo method3 = dbHelperType.GetMethod("Show1", new Type[] { typeof(int) }); //int参数的方法 method3.Invoke(obSqlServerHelper, new object[] { 11 }); MethodInfo method4 = dbHelperType.GetMethod("Show1", new Type[] { typeof(int), typeof(string) }); //调用2个参数的方法,new Type[] { typeof(int), typeof(string) } 顺序一定要跟调用的方法的参数顺序保持一致 method4.Invoke(obSqlServerHelper, new object[] { 1111, "ddd" });
4:调用泛型,首先要创建一个实体类如下:
#region 泛型类 public class GenericClass<T, W, F> { public void Show(T t, W w, F f) { Console.WriteLine($"t.type={t.GetType().Name};}"); } } public class GenericMethod { public void Show<T, W, X>(T t, W w, X x) { Console.WriteLine($"t.type={t.GetType().Name};"); } } public class GenericDouble<T> { public void Show<W, X>(T t, W w, X x) { Console.WriteLine($"t.type={t.GetType().Name};"); } } #endregion
然后调用泛型方法如下:
//创建泛型 Assembly assembly = Assembly.Load("MyReflection"); //获取当前路径下面的dl或者exe,不带后缀 Type genericType = assembly.GetType("MyReflection.GenericClass`3"); //`3是泛型类需要的参数(占位符代表有三个类型参数) Type typeGenericNew = genericType.MakeGenericType(typeof(int), typeof(int), typeof(int)); //需要指定泛型类的类型 GenericClass<int, int, int> oGeneric = (GenericClass<int, int, int>)Activator.CreateInstance(typeGenericNew); oGeneric.Show(1, 30, 60); Type genericType1 = assembly.GetType("MyReflection.GenericMethod"); //普通的类 var genericMethod = Activator.CreateInstance(genericType1) as GenericMethod; genericMethod.Show<int, string, double>(1, "1", 2);
5:调用私有方法(破坏单例)
//私有方法 //调用私有方法,有参数 MethodInfo method5 = dbHelperType.GetMethod("Show5", BindingFlags.NonPublic | BindingFlags.Instance); method5.Invoke(obSqlServerHelper, new object[] { 5.0 }); //私有方法,无参数 MethodInfo method6 = dbHelperType.GetMethod("Show6", BindingFlags.NonPublic | BindingFlags.Instance); method6.Invoke(obSqlServerHelper, null);
6:调用普通方法的泛型方法
//类的泛型方法调用 Type genericMethodType = assembly.GetType("MyReflection.GenericMethod"); var objectGeneric = Activator.CreateInstance(genericMethodType); MethodInfo genericMethod = genericMethodType.GetMethod("Show"); //同样需要指定泛型方法的类型 MethodInfo genericMethodNew = genericMethod.MakeGenericMethod(typeof(int), typeof(int)); //一定要用genericMethodNew进行调用 genericMethodNew.Invoke(objectGeneric, new object[] { 1, 4 });
四:反射的用途:
1:使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及此程序集中查找类型并且创建该类型的实力。
2:使用Module了解包含模块的程序集模块中的类等,还可以获取在模块上定义IDE所有全局方法或其他定义的非全局方法。
3:使用ConstructorInfo 了解构造函数的名字、参数、访问修饰符(如public 或private)和实现详细信息(如abstract或virtual)等。
4:使用MethodInfo 了解方法的名字、返回的类型、参数、参数类型、访问修饰符(如public 或private)和实现详情信息(如abstract或virtual)等。
5:使用FiedInfo了解字段名称、访问修饰符(如public 或private)和实现详细信息(如static)等,并获取或设置字段值。
6:使用PropertyInfo了解属性的名字、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。
7:使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。
8:使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中位置等。
9:使用Attribute了解特性的名字、使用特性的方法属性等。(点击跳转 C# 自定义特性Attribute的使用)
Console.WriteLine("*********************字段和属性***********************"); People people = new People() { ID = 111, Name = "名字", Description = "描述" }; Type typePeople = typeof(People); object oPeople = Activator.CreateInstance(typePeople); //new 一个新的对象 //得到所有的字段public声明,但是么有set和get方法 foreach (var item in typePeople.GetFields()) { Console.WriteLine($"Name1={item.Name};value1={item.GetValue(people)}"); } //得到所有的属性(有set和get的属性) foreach (var item in typePeople.GetProperties()) { if (item.Name.Equals("Id")) { item.SetValue(oPeople, 888); } else if (item.Name.Equals("Name")) { item.SetValue(oPeople, "人民"); } Console.WriteLine($"Name={item.Name};value={item.GetValue(oPeople)}"); }
3:Type类的方法
GetConstructor(),GetConstructors():返回ConstructorInfo类型,用于取得该类的构造函数的信息
GetEvent(),GetEvents():返回EventInfo类型,用于取得该类的事件的信息
GetField(),GetFields():返回FielInfo类型,用于取得该类的字段(成员变量)的信息
GetInterface(),GetInterfaces();返回InterfaceInfo类型,用于获得该类实现的接口信息
GetMember(),GetMembers();返回MemberInfo类型,用于取得该类的所有成员信息
GetMethod(),GetMethods();返回MethodInfo类型,用于取得该类的方法信息
GetProperty(),GetProperties();返回PropertyInfo类型,用于取得该类的属性的信息
六:普通方法和Reflection性能的比较
public class MySqlServerHelper { public MySqlServerHelper() { } public void Query() { } }
public class Monitor { public static void Show() { Console.WriteLine("*******************Monitor*********"); long commonTime = 0; long reflectionTime = 0; { //时间测量工具 Stopwatch watch = new Stopwatch(); watch.Start(); for (int i = 0; i < 1000000; i++) { MySqlServerHelper dbHelper = new MySqlServerHelper(); dbHelper.Query(); } watch.Stop(); commonTime = watch.ElapsedMilliseconds; //毫秒 } { Stopwatch watch = new Stopwatch(); watch.Start(); //1:动态加载 Assembly assembly = Assembly.Load("MyReflection"); //2:获取类型 Type type = assembly.GetType("MyReflection.MySqlServerHelper"); for (int i = 0; i < 1000000; i++) { MySqlServerHelper oDbHelper =(MySqlServerHelper)Activator.CreateInstance(type); oDbHelper.Query(); } watch.Stop(); reflectionTime = watch.ElapsedMilliseconds; } Console.WriteLine($"reflectionTime={reflectionTime};commonTime={commonTime}"); } }
然后调用后发现: 100万次相差30倍左右,所以反射的是很耗性能,但是如果分布到每次则相差几毫秒,所以这个性能影响可以忽略掉!
七:继承后的子类,只需要获取子类的属性:
t.GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); //不要父类的属性,只要子类的属性