反射reflection使用解读

2021-01-14  本文已影响0人  毛毛虫同学

一、 理论

反射Reflection:System.Reflection,是.Net Framework提供的一个帮助类库,可以读取并使用metadata
IL: 也是一种面向对象语言
metadata元数据:数据清单,描述DLL/exe里面的各种信息,CLR运行时会先看metadata

二、 反射使用

#region 普通用法
{
  IDBHelper iDBHelper = new MySqlHelper();
  iDBHelper.Query();
}
#endregion
#region Reflection用法
{
  //1. 动态加载一个完整的dll/exe名称,不需要后缀,从根目类所在的路径进行查找,同名dll和exe,dll优先
  Assembly assemnly = Assembly.Load("DB.MySql");
  //完整路径
  Assembly assemnly = Assembly.LoadFile(@"D:\reflection\bin\debug\DB.MySql".dll);
  //当前路径
  Assembly assemnly = Assembly.LoadFrom("DB.MySql.dll");
  //获取类型,完整类名称
  Type type = assemnly.GetType("DB.MySql.MySqlHelper");
  object oDbHelper = Activator.CreateInstance(type);
  //有参数的构造函数
  object oDbHelper = Activator.CreateInstance(type,new object[]{ 123 });
  //为什么不能直接Query?实际上是有Query方法的,只是因为编译器不认可
  //oDbHelper.Query();

  //类型转换,不报错,类型不对就返回null
  IDbHelper iDbHelper = oDbHelper as IDbHelper;
  iDbHelper.Query();
  //不做类型转换
  var method  = type.GetMethod("Query");
  method.Invoke(oDbHelper,null);
 //有重载方法?
  var method  = type.GetMethod("Query",new Type[]{ typeof(int),typeof(string) });
  method.Invoke(oDbHelper,new object[]{123,"123"});
  //调用带参数方法
   method.Invoke(oDbHelper,new object[]{123});
  //静态方法可以不传实例
   method.Invoke(null,new object[]{123,"123"});

  //dynamic编译器不检查,运行时才检查
  dynamic dDbHelper = Activator.CreateInstance(type);
  dDbHelper.Query();
 
  //查看全部类和方法
  foreach(var type in assembly.GetTypes()
  {
     console.WriteLine(type.Name);//类名
     foreach(var method in type.GetMethods())
     {
        console.WriteLine(method.Name);//方法
     }
     foreach(var construct in type.GetConstructors())
     {
        console.WriteLine(construct .Name);//构造函数
     }
  }
}
#endregion

三、 使用更复杂了?封装工厂

{
  //Reflector + Factory + Config
  //可配置,如果需要换成SqlServer处理时,可以直接改配置文件
  //实现类必须在目录下面
  //可扩展:完全不修改原有代码,只是增加新的实现,复制dll,修改配置文件,就可以支持新功能
  //反射的动态加载和动态创建对象,以及配置文件结合
  IDbHelper iDbHelper = SimpleFactory.CreateInstance();
  iDbHelper.Query();
}
public class SimpleFactory
{
   private static string IDBHelperConfig = ConfigurationManager.AppSetting["IDBHelperConfig"];

   private static string DllName = IDBHelperConfig.Split(',')[0];
   private static string TypeName = IDBHelperConfig.Split(',')[1];
   public static IDBHelper CreateInstance()
   {
       Assembly assemnly = Assembly.Load(DllName);
       Type type = assemnly.GetType(TypeName );
       object oDbHelper = Activator.CreateInstance(type);
       IDbHelper iDbHelper = oDbHelper as IDbHelper;
       return iDbHelper;
   }
}

四、应用场景

1、MVC:浏览器只告诉了服务器Controller(类型)名称和方法名称
MVC在启动时会先加载--扫描全部的dll--找到全部的Controller--存起来--请求来的时候,用Controller来匹配的
MVC局限性--Action如果重载了,反射是无法区分的----MVC如果重载方法只能通过HttpMethod+特性区分HttpPost/HttpGet
2、AOP

//根据dll名称、类型名称、方法名称就可以调用方法

五、延升,反射调用私有方法,调用泛型方法

{
 //调用私有方法
 Assembly assembly = Assembly.Load("DB.SqlServer");
 Type type = assemnly.GetType("DB.MySql.MySqlHelper");
 object oDbHelper = Activator.CreateInstance(type);
 var method = type.GetMethod("Show4",BindingFlag.Instance | BindingFlag.NonPublic);
 method.Invoke(oDbHelper,null);
}
{
//泛型方法
 Assembly assembly = Assembly.Load("DB.SqlServer");
 Type type = assemnly.GetType("DB.MySql.MySqlHelper");
 object oDbHelper = Activator.CreateInstance(type);
 var method = type.GetMethod("Show");
 method.MakeGenericMethod(new Type[]{typeof(string),typeof(int)});
 method.Invoke(oDbHelper,new object[]{"123",123});
}
{
//泛型类下的泛型方法
 Assembly assembly = Assembly.Load("DB.SqlServer");
 Type type = assemnly.GetType("DB.MySql.MySqlHelper`1").MakeGenericType(typeof(int));
 object oDbHelper = Activator.CreateInstance(type);
 var method = type.GetMethod("Show");
 method.MakeGenericMethod(new Type[]{typeof(string),typeof(int)});
 method.Invoke(oDbHelper,new object[]{"123",123});
}
image.png

六、总结
反射的优缺点:
优点:动态
缺点:1.使用麻烦(封装解决) 2.避开编译器检查 3.性能问题
测试:100万次循环,普通方法 41ms,反射 6512ms
但是换个角度分析下,100次循环,反射耗时0.65ms
反射可以缓存优化,dll加载和类型获取只执行一次,
100万次循环,普通方法 48ms 反射 103ms

MVC--Asp.net--ORM--IOC--AOP都在用反射,几乎都用缓存
MVC&ORM 启动都很慢,完成了很多初始化,反射信息,后面启动就很快

上一篇下一篇

猜你喜欢

热点阅读