一、反射是什么
1、C#编译运行过程
高级语言->编译->dll/exe文件->CLR/JIT->机器码
2、原理解析
metadata:元数据数据清单,记录了dll中包含了哪些东西,是一个描述。
IL:中间语言,编译把高级语言编译后得到的C#中最真实的语言状态,面向对象语言。
反射:来自于System.Reflection,是一个帮助类库,可以读取dll/exe中metadata,使用metadata创建对象。
Emit:一种反射技术,可以动态创建dll/exe。
反编译工具:ILSpy可以反编译dll/exe,查看对应的C#/IL代码。
二、反射创建对象
1、动态读取dll
- LoadFrom:dll全名称,需要后缀
- LoadFile:全路径,需要dll后缀
- Load:dll名称不需要后缀
//1、动态读取dll的三种方式 //(1)LoadFrom:dll全名称,需要后缀 Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll"); //(2)LoadFile:全路径,需要dll后缀 //Assembly assembly1 = Assembly.LoadFile(@"dll文件全路径"); //(3)Load:dll名称 不需要后缀 //Assembly assembly2 = Assembly.Load("Business.DB.SqlServer");
2、获取类型
//2、获取某一个具体的类型,参数需要是类的全名称 Type type1 = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");
3、创建对象
- 直接传类型
- 重载方法,传dll的全名称
- 返回值是object类型,不能直接调用方法
//3、创建对象 //(1)直接传类型 object? oInstance = Activator.CreateInstance(type1); //(2)重载方法,传dll的全名称 //object? oInstanc1= Activator.CreateInstance("Business.DB.SqlServer.dll", "Business.DB.SqlServer.SqlServerHelper"); //a.oInstance.Query();//报错了:因为oInstance当做是一个object类型,object类型是没有Query方法;C#语言是一种强类型语言;编译时决定你是什么类型,以左边为准;不能调用是因为编译器不允许;实际类型一定是SqlServerHelper; //b.如果使用dynamic 作为类型的声明,在调用的时候,没有限制; //c.dynamic :动态类型:不是编译时决定类型,避开编译器的检查;运行时决定是什么类型 //d.dynamic dInstance = Activator.CreateInstance(type); //e.dInstance.Query(); //f.dInstance.Get(); //报错了--因为SqlServerHelper没有Get方法
4、类型转换
//4、类型转换 // SqlServerHelper helper = (SqlServerHelper)oInstance; //不建议这样转换--如果真实类型不一致--会报报错; IDBHelper helper = oInstance as IDBHelper;//如果类型一直,就转换,如果不一致;就返回null
三、反射创建对象详解
using System; namespace MyReflecttion { /// <summary> /// 反射测试类 /// </summary> public class ReflectionTest { #region Actor /// <summary> /// 无参构造函数 /// </summary> public ReflectionTest() { Console.WriteLine($"这里是{this.GetType()} 无参数构造函数"); } /// <summary> /// 带参数构造函数 /// </summary> /// <param name="name"></param> public ReflectionTest(string name) { Console.WriteLine($"这里是{this.GetType()} 有参数构造函数"); } public ReflectionTest(int id) { Console.WriteLine($"这里是{this.GetType()} 有参数构造函数"); } public ReflectionTest(int id, string name) { Console.WriteLine($"这里是{this.GetType()} 有参数构造函数"); } public ReflectionTest(string name,int id ) { Console.WriteLine($"这里是{this.GetType()} 有参数构造函数"); } #endregion #region Method /// <summary> /// 无参方法 /// </summary> public void Show1() { Console.WriteLine($"这里是{this.GetType()}的Show1" ); } /// <summary> /// 有参数方法 /// </summary> /// <param name="id"></param> public void Show2(int id) { Console.WriteLine($"这里是{this.GetType()}的Show2"); } /// <summary> /// 重载方法之一 /// </summary> /// <param name="id"></param> /// <param name="name"></param> public void Show3(int id, string name) { Console.WriteLine($"这里是{this.GetType()}的Show3"); } /// <summary> /// 重载方法之二 /// </summary> /// <param name="name"></param> /// <param name="id"></param> public void Show3(string name, int id) { Console.WriteLine($"这里是{this.GetType()}的Show3_2"); } /// <summary> /// 重载方法之三 /// </summary> /// <param name="id"></param> public void Show3(int id) { Console.WriteLine($"这里是{this.GetType()}的Show3_3"); } /// <summary> /// 重载方法之四 /// </summary> /// <param name="name"></param> public void Show3(string name) { Console.WriteLine($"这里是{this.GetType()}的Show3_4"); } /// <summary> /// 重载方法之五 /// </summary> public void Show3() { Console.WriteLine($"这里是{this.GetType()}的Show3_1"); } /// <summary> /// 私有方法 /// </summary> /// <param name="name"></param> private void Show4(string name) //肯定是可以的 { Console.WriteLine($"这里是{this.GetType()}的Show4"); } /// <summary> /// 静态方法 /// </summary> /// <param name="name"></param> public static void Show5(string name) { Console.WriteLine($"这里是{typeof(ReflectionTest)}的Show5"); } #endregion } }
泛型方法泛型类
using System; namespace MyReflecttion { public class GenericMethod { public void Show<T, W, X>(T t, W w, X x) { Console.WriteLine($"t.type={t.GetType().Name},w.type={ w.GetType().Name},x.type={x.GetType().Name}"); } } public class GenericClass<T, W, X> { public void Show(T t, W w, X x) { Console.WriteLine($"t.type={t.GetType().Name},w.type={w.GetType().Name},x.type={x.GetType().Name}"); } } public class GenericDouble<T> { public void Show<W, X>(T t, W w, X x) { Console.WriteLine($"t.type={t.GetType().Name},w.type={w.GetType().Name},x.type={x.GetType().Name}"); } } }
1、创建对象
(1)调用无参数构造函数的
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll"); Type type = assembly.GetType("MyReflecttion.ReflectionTest"); object noParaObject = Activator.CreateInstance(type);
(2)调用有参数构造函数的
需要传递一个object类型的数组作为参数,参数按照从昨往右严格匹配,如果没有匹配的报异常
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll"); Type type = assembly.GetType("MyReflecttion.ReflectionTest"); object paraObject = Activator.CreateInstance(type, new object[] { 123 }); object paraObject1 = Activator.CreateInstance(type, new object[] { "三三" }); object paraObject2 = Activator.CreateInstance(type, new object[] { 234, "四四" }); object paraObject3 = Activator.CreateInstance(type, new object[] { "五五", 456 });
四、反射调用方法详解
获取方法MethodInfo,执行MethodInfo 的Invoke方法,传递方法所在的类的实例对象+参数
1、调用无参数的方法
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll"); Type type = assembly.GetType("MyReflecttion.ReflectionTest"); object oInstance = Activator.CreateInstance(type); MethodInfo show1 = type.GetMethod("Show1"); show1.Invoke(oInstance, new object[] { }); show1.Invoke(oInstance, new object[0]); show1.Invoke(oInstance, null);
2、调用有参数的方法
需要通过方法参数类型类区别方法,传递参数,严格匹配参数类型
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll"); Type type = assembly.GetType("MyReflecttion.ReflectionTest"); object oInstance = Activator.CreateInstance(type); MethodInfo show2 = type.GetMethod("Show2"); show2.Invoke(oInstance, new object[] { 123 }); MethodInfo show31 = type.GetMethod("Show3", new Type[] { typeof(string), typeof(int) }); show31.Invoke(oInstance, new object[] { "一一一", 234 }); MethodInfo show32 = type.GetMethod("Show3", new Type[] { typeof(int) }); show32.Invoke(oInstance, new object[] { 345 }); MethodInfo show33 = type.GetMethod("Show3", new Type[] { typeof(string) }); show33.Invoke(oInstance, new object[] { "二二二" }); MethodInfo show34 = type.GetMethod("Show3", new Type[0]); show34.Invoke(oInstance, null);
3、调用私有方法
在获取方法的时候,加上参数BindingFlags.NonPublic | BindingFlags.Instance
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll"); Type type = assembly.GetType("MyReflecttion.ReflectionTest"); object oInstance = Activator.CreateInstance(type); MethodInfo show4 = type.GetMethod("Show4", BindingFlags.NonPublic | BindingFlags.Instance); show4.Invoke(oInstance, new object[] { "String" });
4、调用静态方法
不需要创建对象也可以调用
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll"); Type type = assembly.GetType("MyReflecttion.ReflectionTest"); MethodInfo show5 = type.GetMethod("Show5"); show5.Invoke(null, new object[] { "String" });
5、调用普通类的泛型方法
获取到泛型方法后需要先确定类型
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll"); Type type = assembly.GetType("MyReflecttion.GenericMethod"); object oInstance = Activator.CreateInstance(type); MethodInfo show = type.GetMethod("Show"); //获取到泛型方法后需要先确定类型 MethodInfo genericshow = show.MakeGenericMethod(new Type[] { typeof(int), typeof(string), typeof(DateTime) }); genericshow.Invoke(oInstance, new object[] { 123, "三三三", DateTime.Now });
6、调用泛型类的普通方法
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll"); //泛型类的类型需要在类后面加占位符 Type type = assembly.GetType("MyReflecttion.GenericClass`3"); //泛型类获取到类型后需要先确定类型 Type generType = type.MakeGenericType(new Type[] { typeof(int), typeof(string), typeof(DateTime) }); object oInstance = Activator.CreateInstance(generType); MethodInfo show = generType.GetMethod("Show"); show.Invoke(oInstance, new object[] { 123, "四四四", DateTime.Now });
7、调用泛型类的泛型方法
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll"); //泛型类的类型需要在类后面加占位符 Type type = assembly.GetType("MyReflecttion.GenericDouble`1"); //泛型类获取到类型后需要先确定类型 Type generType = type.MakeGenericType(new Type[] { typeof(int) }); object oInstance = Activator.CreateInstance(generType); MethodInfo show = generType.GetMethod("Show"); //获取到泛型方法后需要先确定类型 MethodInfo genericMethod = show.MakeGenericMethod(new Type[] { typeof(string), typeof(DateTime) }); genericMethod.Invoke(oInstance, new object[] { 123, "五五五", DateTime.Now });
五、反射操作属性字段
- 普通方法调用属性字段简单快捷,反射操作麻烦点。
- 类增加一个字段呢,普通方法调用需要修改代码,重新编译发布,代码不稳定,反射赋值没啥优势,反射取值不需要修改代码,代码就更加稳定。
- type.GetProperties()获取属性,type.GetFields()获取字段。
1、创建测试类
using System; namespace Business.DB.Model { /// <summary> /// 实体---属性是不能保存数据,只有字段才能保存数据 /// </summary> public class People { public People() { Console.WriteLine("{0}被创建", this.GetType().FullName); } public int Id { get; set; }//带有Get Set 方法的叫做属性 public string Name { get; set; } public int Age { get; set; } public string Description;//字段 } }
2、传统方式赋值取值
//传统手艺赋值取值 Console.WriteLine("***********传统手艺赋值取值*************"); People people = new People(); people.Id = 134; people.Name = "WWWW"; people.Age = 25; people.Description = "XXXXX"; Console.WriteLine($"people.Id={people.Id}"); Console.WriteLine($"people.Name={people.Name}"); Console.WriteLine($"people.Age={people.Age}"); Console.WriteLine($"people.Description={people.Description}");
3、反射方式赋值取值
//反射方式赋值取值 Console.WriteLine("***********反射方式赋值取值*************"); Type type = typeof(People); object pObject = Activator.CreateInstance(type); foreach (var prop in type.GetProperties()) { if (prop.Name.Equals("Id")) { prop.SetValue(pObject, 134); } else if (prop.Name.Equals("Name")) { prop.SetValue(pObject, "WWWW"); } else if (prop.Name.Equals("Age")) { prop.SetValue(pObject, 25); } } foreach (var prop in type.GetProperties()) { Console.WriteLine($"people.{prop.Name}={prop.GetValue(pObject)}"); }
4、运行结果
***********传统手艺赋值取值************* Business.DB.Model.People被创建 people.Id=134 people.Name=WWWW people.Age=25 people.Description=XXXX ***********反射方式赋值取值************* Business.DB.Model.People被创建 people.Id=134 people.Name=WWWW people.Age=25
六、反射的局限/性能问题
1、性能对比代码实现
using Business.DB.Interface; using Business.DB.SqlServer; using System; using System.Diagnostics; using System.Reflection; namespace MyReflecttion { 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 < 1000_000; i++) //1000000000 { IDBHelper iDBHelper = new SqlServerHelper(); iDBHelper.Query(); } watch.Stop(); commonTime = watch.ElapsedMilliseconds; } { Stopwatch watch = new Stopwatch(); watch.Start(); //优化代码,加载dll放到循环外面 Assembly assembly = Assembly.Load("Business.DB.SqlServer");//1 动态加载 Type dbHelperType = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");//2 获取类型 for (int i = 0; i < 1000_000; i++) { //创建对象+方法调用 object oDBHelper = Activator.CreateInstance(dbHelperType);//3 创建对象 IDBHelper dbHelper = (IDBHelper)oDBHelper;//4 接口强制转换 dbHelper.Query();//5 方法调用 } watch.Stop(); reflectionTime = watch.ElapsedMilliseconds; } Console.WriteLine($"commonTime={commonTime} reflectionTime={reflectionTime}"); } } }
2、运行结果
- 测试用例:普通方式循环100000次,创建对象+方法调用:16毫秒
反射方式循环100000次,加载dll+创建对象+方法调用:6300毫秒 - 加载dll放到循环外面,创建对象+方法调用放循环里面,泛型方法:57毫秒
3、使用反射的建议
反射确实有性能问题,但是差别没有那么大,在需要的地方可以放心使用
标签:反射,object,Console,C#,GetType,dll,详解,type,public From: https://www.cnblogs.com/lvbjj/p/18115974