反射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 启动都很慢,完成了很多初始化,反射信息,后面启动就很快