Open Liao9144 opened 6 years ago
反射提供描述程序集、模块和类型的对象(Type 类型)。可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型,然后调用其方法或访问器字段和属性。如果代码中使用了特性,可以利用反射来访问它们。
下图是 .net 程序开发到程序运行的整个过程
反射(System.Reflection)是.Net框架提供帮助类库,可以读取并使用元数据(Metadata)。 元数据(Metadata)描述了程序集的内容。通过将元数据嵌入每个程序集中,任何程序集都可以实现完全的自描述,从而简化了发布使用较旧技术的组件时所需进行的工作。
项目结构,MyReflection 没有引用 MyReflection.Model。
public class UserInfo<T> where T : struct { private T _id; // 字段 [Display(Name = "ID")] // 特性 public T Id // 属性 { get { return _id; } set { _id = value; } } [Display(Name = "姓名")] // 特性 public string Name { get; set; } // 属性 [Display(Name = "年龄")] public int Age { get; set; } // 属性 public UserInfo() // 无参构造函数 { Console.WriteLine("这里是{0}的无参构造函数", this.GetType()); } public UserInfo(T id) // 带参数构造函数 { _id = id; Console.WriteLine("这里是{0}的带参数构造函数,参数id是{1}", this.GetType(), id); } public void Show1() // 无参方法 { Console.WriteLine("这里是{0}的无参方法Show1", this.GetType()); } public void Show2(int parameter) // 有参方法 { Console.WriteLine("这里是{0}的有参方法Show2,参数parameter是{1}", this.GetType(), parameter); } public void Show3(int parameter) // 重载方法之一 { Console.WriteLine("这里是{0}的重载方法之一Show3,参数parameter是{1}_{2}", this.GetType(), parameter, parameter.GetType()); } public void Show3(string parameter) // 重载方法之二 { Console.WriteLine("这里是{0}的重载方法之二Show3,参数parameter是{1}_{2}", this.GetType(), parameter, parameter.GetType()); } private void Show4() // 私有方法 { Console.WriteLine("这里是{0}的私有方法Show4", this.GetType()); } public static void Show5() // 静态方法 { Console.WriteLine("这里是{0}的静态方法Show5", typeof(UserInfo<T>)); } public void Show6<P1, P2>(P1 parameter1, P2 parameter2) // 泛型方法 { Console.WriteLine("这里是{0}的泛型方法Show6,参数parameter是{1}_{2}&{3}_{4}", this.GetType(), parameter1, parameter1.GetType(), parameter2, parameter2.GetType()); } }
a. 加载程序集(加载不会错,但是如果没有依赖项,使用的时候会错)
//Assembly assembly = Assembly.Load("MyReflection.Model"); // 从当前目录加载(dll名称无后缀) Assembly assembly = Assembly.LoadFile(@"E:\MyGitHub\Blog\.NET 高级开发系列\MyReflection\MyReflection.Model\bin\Debug\MyReflection.Model.dll"); // 完整路径加载 //Assembly assembly = Assembly.LoadFrom("MyReflection.Model.dll"); // 从当前目录加载(dll名称带后缀)或者完整路径加
b. 获取类型信息(根据类的全名称(命门空间.类名)获取类的信息,泛型根据泛型参数个数(n个)加上 `n)
Type type = assembly.GetType("MyReflection.Model.UserInfo`1"); type = type.MakeGenericType(new Type[] { typeof(int) }); // 注意要重新接收
c. 创建对象
// object instance = Activator.CreateInstance(type); // 调用无参构造函数 object instance = Activator.CreateInstance(type, new object[] { 1000 }); // 调用有参数构造
d. 读取私有字段
object _id = type.GetField("_id", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(instance);
e. 设置属性值
type.GetProperty("Name").SetValue(instance, "张三"); type.GetProperty("Age").SetValue(instance, 18);
f. 读取属性值
DisplayAttribute idDisplay = (DisplayAttribute)type.GetProperty("Id").GetCustomAttribute(typeof(DisplayAttribute)); DisplayAttribute nameDisplay = (DisplayAttribute)type.GetProperty("Name").GetCustomAttribute(typeof(DisplayAttribute)); DisplayAttribute ageDisplay = (DisplayAttribute)type.GetProperty("Age").GetCustomAttribute(typeof(DisplayAttribute));
g. 读取属性特性 DisplayAttribute idDisplay = (DisplayAttribute)type.GetProperty("Id").GetCustomAttribute(typeof(DisplayAttribute)); DisplayAttribute nameDisplay = (DisplayAttribute)type.GetProperty("Name").GetCustomAttribute(typeof(DisplayAttribute)); DisplayAttribute ageDisplay = (DisplayAttribute)type.GetProperty("Age").GetCustomAttribute(typeof(DisplayAttribute));
h. 调用方法
var show1 = type.GetMethod("Show1"); show1.Invoke(instance, null); // 没有参数null或者new object[] { } var show2 = type.GetMethod("Show2"); show2.Invoke(instance, new object[] { 111 }); var show3_1 = type.GetMethod("Show3", new Type[] { typeof(int) }); show3_1.Invoke(instance, new object[] { 123 }); var show3_2 = type.GetMethod("Show3", new Type[] { typeof(string) }); show3_2.Invoke(instance, new object[] { "abc" }); var show4 = type.GetMethod("Show4", BindingFlags.NonPublic | BindingFlags.Instance); show4.Invoke(instance, null); var show5 = type.GetMethod("Show5"); //show5.Invoke(instance, null); show5.Invoke(null, null); // 静态方法不用实例 var show6 = type.GetMethod("Show6"); show6 = show6.MakeGenericMethod(new Type[] { typeof(int), typeof(string) }); // 注意要重新接收 show6.Invoke(instance, new object[] { 123, "abc" });
运行结果
性能问题其实可以说一下,先看下面的性能对比
public static void Show() { long commonTime = 0; long reflectionTime = 0; { Stopwatch watch = new Stopwatch(); watch.Start(); for (int i = 0; i < 1000000; i++) { List<int> list = new List<int>(); list.Clear(); } watch.Stop(); commonTime = watch.ElapsedMilliseconds; } { Stopwatch watch = new Stopwatch(); watch.Start(); for (int i = 0; i < 1000000; i++) { Assembly assembly = Assembly.Load("mscorlib"); // 1.动态加载 Type type = assembly.GetType("System.Collections.Generic.List`1"); // 2.获取类型 type = type.MakeGenericType(new Type[] { typeof(int) }); object instance = Activator.CreateInstance(type); // 3.创建对象 List<int> list = (List<int>)instance; // 4.接口强制转换 list.Clear(); // 5.方法调用 } watch.Stop(); reflectionTime = watch.ElapsedMilliseconds; } Console.WriteLine("commonTime={0}ms,reflectionTime={1}ms", commonTime, reflectionTime); }
100w 次调用 13ms vs 5242ms,相差 400 多倍,看起来是挺吓人的,但如果是 100 次调用其实反射方式也只是用了0.5242ms,1ms都不到,做一次SQL查询也比这个大得多,绝对值是很小的。其实上述代码可以把1.动态加载&2.获取类型放到for外面,程序集和类型这些都是固定不变的,程序加载一次就够了(空间换时间)。
Stopwatch watch = new Stopwatch(); watch.Start(); Assembly assembly = Assembly.Load("mscorlib"); // 1.动态加载 Type type = assembly.GetType("System.Collections.Generic.List`1"); // 2.获取类型 type = type.MakeGenericType(new Type[] { typeof(int) }); for (int i = 0; i < 10000; i++) { //Assembly assembly = Assembly.Load("mscorlib"); // 1.动态加载 //Type type = assembly.GetType("System.Collections.Generic.List`1"); // 2.获取类型 //type = type.MakeGenericType(new Type[] { typeof(int) }); object instance = Activator.CreateInstance(type); // 3.创建对象 List<int> list = (List<int>)instance; // 4.接口强制转换 list.Clear(); // 5.方法调用 } watch.Stop(); reflectionTime = watch.ElapsedMilliseconds;
看结果现在只是差了4倍多,那绝对值更小了。反射在绝大部分情况不影响你的程序性能,如果你程序追求极致的性能把所有的反射去掉也不会带来多大的性能提升(性能优化是要抓住大头,影响最大的地方去优化),另外在 ORM、MVC、IOC、AutoMapper 都是大量在使用反射技术的。
一、反射概述
1.什么是反射
反射提供描述程序集、模块和类型的对象(Type 类型)。可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型,然后调用其方法或访问器字段和属性。如果代码中使用了特性,可以利用反射来访问它们。
2.反射原理
下图是 .net 程序开发到程序运行的整个过程
反射(System.Reflection)是.Net框架提供帮助类库,可以读取并使用元数据(Metadata)。 元数据(Metadata)描述了程序集的内容。通过将元数据嵌入每个程序集中,任何程序集都可以实现完全的自描述,从而简化了发布使用较旧技术的组件时所需进行的工作。
二、反射的使用
1.常用 API
2.API 应用
项目结构,MyReflection 没有引用 MyReflection.Model。
a. 加载程序集(加载不会错,但是如果没有依赖项,使用的时候会错)
b. 获取类型信息(根据类的全名称(命门空间.类名)获取类的信息,泛型根据泛型参数个数(n个)加上 `n)
c. 创建对象
d. 读取私有字段
e. 设置属性值
f. 读取属性值
g. 读取属性特性 DisplayAttribute idDisplay = (DisplayAttribute)type.GetProperty("Id").GetCustomAttribute(typeof(DisplayAttribute)); DisplayAttribute nameDisplay = (DisplayAttribute)type.GetProperty("Name").GetCustomAttribute(typeof(DisplayAttribute)); DisplayAttribute ageDisplay = (DisplayAttribute)type.GetProperty("Age").GetCustomAttribute(typeof(DisplayAttribute));
h. 调用方法
运行结果
三、反射的优缺点
性能问题其实可以说一下,先看下面的性能对比
运行结果
100w 次调用 13ms vs 5242ms,相差 400 多倍,看起来是挺吓人的,但如果是 100 次调用其实反射方式也只是用了0.5242ms,1ms都不到,做一次SQL查询也比这个大得多,绝对值是很小的。其实上述代码可以把1.动态加载&2.获取类型放到for外面,程序集和类型这些都是固定不变的,程序加载一次就够了(空间换时间)。
运行结果
看结果现在只是差了4倍多,那绝对值更小了。反射在绝大部分情况不影响你的程序性能,如果你程序追求极致的性能把所有的反射去掉也不会带来多大的性能提升(性能优化是要抓住大头,影响最大的地方去优化),另外在 ORM、MVC、IOC、AutoMapper 都是大量在使用反射技术的。