C#反射
2020-12-17 21:35
标签:day binding 怎样 lag 访问 void open user rtu 1.What?反射是什么? 反射:无处不在,MVC,Webfrom,asp.net,ORM,IOC,AOP,几乎所有的框架都离不开反射,那么反射到底是什么? 我们写的代码,计算机要识别,需要二次编译,中间会经过编译器编译,得到dll,exe,再被JIT编译最终被计算机语言识别,执行,那dll,exe是怎么生成的呢? 生成的exe还可以直接打开执行 那这个exe里面具体是什么呢?我们可以用反编译工具打开看看,打开就可以看到我们反编译以后的IL:也是一种面向对象语言,但是不太好阅读 反射Reflection:system.Reflection,其实就是.net.fromwork提供的一个帮助内库,可以读取并使用metadata 那我们如何去读取信息呢? 我们先随便写个接口类:新建-DB.Interface类库,新建一个接口类IDBHelper 再新建一个DB.MySql类库,新建一个MySqlHelper类继承接口IDBHelper 按照上面的步骤,新建一个DB.SqlServer类库,新建一个SqlServerHelper类继承接口IDBHelper 我们跟踪就可以看到利用反射加载出来的一些基本信息 那么问题来了我只需要用普通方法两句话写完的,我们为什么要用反射要写5句???何必呢?意义何在? 我们就上面的代码先来封装一下看看: 那我们再来调用看看: 这是普通方法和反射封装完成调用 的结果以及代码对比,从调用上是不是差不多了呢? 我们再进一步对比延伸扩展一下: 如果我们需要更换一下版本呢? 普通方法:【就必须重新引用,修改代码,重新编译发布】 而我们的反射,只需修改配置文件即可: 结果: 那要是我们要新增一个以前没有的Oracle版本: 按照上面的步骤,新建一个DB.Oracle类库,新建一个OracleHelper类继承接口IDBHelper 用反射,通过修改配置文件就可以达到以下结果: 结果: 上面我们用反射获取了无参数的构造函数,那么我们怎么去获取有参数的构造函数呢?我们怎么去获取构造函数参数,以及类型,调用? 让我们看一个例子,在DB.SqlServer类库中,新建一个ReflecgionTest类 结果: 让我们看一个例子,在DB.SqlServer类库中,新建一个Singleton类 结果对比: 让我们看一个例子,在DB.SqlServer类库中,新建一个GenericTest类 按照之前的操作,我们发现这边得到的type=null?Why? 跟踪结果已经找到了我们要的数据了: 再执行,报错了,类型找到了,为什么它不能创建这个实例?它不是有个无参数构造函数吗?为什么没能创建一个实例? 那这代码现在应该如何修改呢? 跟踪的结果: 如果反射创建对象之后,知道方法名称,怎么样不做类型转换,直接调用方法?让我们直接下面的实例吧 结果: 结果: 结果: 获取全部的实体的全部属性并给其赋值:2.用反射加载和读取信息[三种方式]
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
7 namespace DB.Interface
8 {
9 // DB.Interface接口类库
10 public interface IDBHelper
11 {
12 void Query();
13 }
14 }
using DB.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DB.MySql
{
public class MySqlHelper : IDBHelper
{
///
using DB.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DB.SqlServer
{
///
#region Reflecgion
{
Console.WriteLine("---------------------------------------------反射加载和读取信息---------------------------------------------------------");
//动态加载 都是把dll加到内存里面去,不需要向上面的普通方法一样引用
//1.一个完整的dll名称,不需要后缀,目的:把dll加载到内存中间去;
//缺陷:但是有个限制从编译以后生成的exe所在的路径去查找,既可以找dll,又可以找exe,两个一样得话先找dll【常用】
Assembly assembly = Assembly.Load("DB.MySql");
//2.还有一种加载方式,path,需要一个完整的路径,性能差不多,也可以切换为别的引用了的路径【全名称= 全路径+dll名称 + 后缀】
Assembly assembly1 = Assembly.LoadFile(@"F:\LearnTest\MyReflectionFirst\MyReflectionFirst\bin\Debug\DB.MySql.dll");
//3.loadfrom:带后缀的,当前路径查找,也可以全路径
Assembly assembly2 = Assembly.LoadFrom("DB.MySql.dll");//【当前路径+ 后缀】
Assembly assembly3 = Assembly.LoadFrom(@"F:\LearnTest\MyReflectionFirst\MyReflectionFirst\bin\Debug\DB.MySql.dll");
//读取里面的信息
foreach (var types in assembly.GetTypes())//GetTypes 获取此程序集中的全部类型
{
Console.WriteLine(types.Name);
//type.IsGenericType //判断当前是否是泛型类
foreach (var method in types.GetMethods())//返回为GetMethods的所有公共方法
{
Console.WriteLine(method.Name);
}
}
}
#endregion3.反射使用信息
#region Common
{
Console.WriteLine("---------------------------------------------Common---------------------------------------------------------");
{
//添加接口引用,内库引用,实例化并且调用方法
IDBHelper dBHelper = new MySqlHelper();
dBHelper.Query();
}
}
#endregion
#region Reflecgion
{
Console.WriteLine("---------------------------------------------用反射使用信息---------------------------------------------------------");
//1.动态加载
Assembly assembly = Assembly.Load("DB.MySql");
// 第一步我们会获取到全部的类型,也可以指定获取类型【完整类型名称】
Type type = assembly.GetType("DB.MySql.MySqlHelper");
//3.实例化
// 意思跟Activator.CreateInstance(type);差不多都是实例化new MySqlHelper();但是这边创建的实例是个object类型
object oDBHelper=Activator.CreateInstance(type);
//很确定oDBHelper里面有Query这个方法Why不能直接调用方法?编译器不认可,不能直接调用
//c#是一门强类型语言,静态语言,编译时就确定好了类型确保安全
//oDBHelper.Query()
//dynamic可以是因为它很特殊编译器不检查,运行时才检查
//dynamic dDBHelper = Activator.CreateInstance(type);
//dDBHelper.Query();
//4.类型转换,类型不对会直接返回null
IDBHelper dBHelper = oDBHelper as IDBHelper;
//5.方法调用
dBHelper.Query();
}
#endregion
1
namespace MyReflectionFirst
{
public class SingleFactory
{
///
{
Console.WriteLine("-------------------------------------Reflecgion+Factory+Config-------------------------------------------------");
IDBHelper dBHelper = SingleFactory.CreateInstance();
dBHelper.Query();
}
//如果从MySqlHelper换成SqlServerHelper,就必须重新引用,修改代码,重新编译发布
IDBHelper dBHelper = new SqlServerHelper();
dBHelper.Query();
using DB.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DB.Oracle
{
public class OracleHelper : IDBHelper
{
public OracleHelper()
{
Console.WriteLine("{0}被构造",this.GetType().Name);
}
public void Query()
{
Console.WriteLine("{0}.Query",this.GetType().Name);
}
}
}
//特点:动态扩展性太强
//可配置可扩展:完全不修改原有代码,只是增加新的实现,copy,修改配置文件,就可以支持新功能
//原理:反射的动态加载和动态创建对象,以及配置文件结合
//可以支持随机换,也可以后面新增
//前提:实现类必须是事先已有的,而且在目录下面的
4.反射的使用
public class ReflecgionTest
{
#region Identity
public ReflecgionTest()
{
Console.WriteLine("这是{0}无参数构造函数", this.GetType());
}
public ReflecgionTest(string name)
{
Console.WriteLine("这是{0}有参数构造函数", this.GetType());
}
public ReflecgionTest(int id)
{
Console.WriteLine("这是{0}有参数构造函数", this.GetType());
}
#endregion
}
{
Console.WriteLine("-----------------------------------ctor¶meter---------------------------------------------------");
Assembly assembly = Assembly.Load("DB.SqlServer");
Type type = assembly.GetType("DB.SqlServer.ReflecgionTest");
//获取构造函数
foreach (ConstructorInfo ctor in type.GetConstructors())//循环访问集合或数组的名称
{
Console.WriteLine(ctor.Name);
//获取构造函数参数
foreach (var parameter in ctor.GetParameters())
{
//获取构造函数参数类型
Console.WriteLine(parameter.ParameterType);
}
}
//怎么为不同的构造函数指定不同的参数类型
//object test1 = Activator.CreateInstance(type);
//object test2 = Activator.CreateInstance(type, "123");
//object test3 = Activator.CreateInstance(type, 123);
//应用建议改为以下写法
object test4 = Activator.CreateInstance(type, new object[] { "123" });
object test5= Activator.CreateInstance(type, new object[] { 123});
}
5.反射-黑科技:反射破坏单例
namespace DB.SqlServer
{
///
{
Console.WriteLine("-----------------------------------扩展:未使用反射之前的单例调用---------------------------------------------------");
//Singleton singleton = new Singleton();//私有化不能直接构造
Singleton singleton1 = Singleton.GetInstance();
Singleton singleton2 = Singleton.GetInstance();
Singleton singleton3 = Singleton.GetInstance();
Console.WriteLine($"{Object.ReferenceEquals(singleton1, singleton3)}");//不管有几个实例,结果肯定为True,因为都是同一个对象,只会被构造一次
}
{
Console.WriteLine("-----------------------------------扩展:反射破坏单例---反射调用私有构造函数---------------------------------------------------");
Assembly assembly = Assembly.Load("DB.SqlServer");
Type type = assembly.GetType("DB.SqlServer.Singleton");
//Singleton singletonA=(Singleton)Activator.CreateInstance(type);//类型转换
//如上写法会报错,因为上面我们说过它相当于一个实例化,但是这个构造函数私有化,不能直接New
Singleton singletonA = (Singleton)Activator.CreateInstance(type,true);//但是反射做到了这件事
//Why这个地方执行了第一次使用的时候会执行两遍构造函数,程序在一次使用这个类的时候就会去完成静态构造函数的构造,程序在每次使用都会再去构造一次
Singleton singletonB = (Singleton)Activator.CreateInstance(type, true);
Singleton singletonC = (Singleton)Activator.CreateInstance(type, true);
//nonPublic:如果公共或非公共默认构造函数可以匹配,则为true;如果只有公共默认构造函数可以匹配,则为false。
//所以对象是不一样的,对象被构造了多次
Console.WriteLine($"{Object.ReferenceEquals(singletonA,singletonC)}");
}
六.反射调用泛型类与方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DB.SqlServer
{
public class GenericTest
{
Console.WriteLine("-----------------------------------扩展:反射和泛型---------------------------------------------------");
Assembly assembly = Assembly.Load("DB.SqlServer");
Type type = assembly.GetType("DB.SqlServer.GenericTest");
Object oGeneric = Activator.CreateInstance(type);
}
//原因:因为public class GenericTest
// 我们需要把它修改为:
Type type = assembly.GetType("DB.SqlServer.GenericTest`3");
//在泛型类型时,不能忘记我们创建对象,它是不能够实例化的,【eg:GenericTest genericTest=new GenericTest()】那肯定是不行的
//起码你得指定好类型才能够实例化【eg:GenericTest
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DB.SqlServer
{
public class ReflecgionTest
{
#region Identity
public ReflecgionTest()
{
Console.WriteLine("这是{0}无参数构造函数", this.GetType());
}
public ReflecgionTest(string name)
{
Console.WriteLine("这是{0}有参数构造函数", this.GetType());
}
public ReflecgionTest(int id)
{
Console.WriteLine("这是{0}有参数构造函数", this.GetType());
}
#endregion
#region Method
///
反射调用:无参数,有参数,重载,静态,私有方法实例
{
Console.WriteLine("如果反射创建对象之后,知道方法名称,怎么样不做类型转换,直接调用方法?");
Assembly assembly = Assembly.Load("DB.SqlServer");
Type type = assembly.GetType("DB.SqlServer.ReflecgionTest");
Object test = Activator.CreateInstance(type);
//主要是查看它里面的所有方法名,参数,参数类型
foreach (var metohd in type.GetMethods())
{
Console.WriteLine(metohd.Name+":");
foreach (var parameter in metohd.GetParameters())
{
Console.WriteLine($"{parameter.Name},{parameter.ParameterType.Name}");
}
}
//反射调用无参数的方法
{
MethodInfo methodInfo = type.GetMethod("Show1");
methodInfo.Invoke(test, null);//第一个参数表示实例,后一个参数列表
}
//反射调用带参数的方法
{
MethodInfo methodInfo1 = type.GetMethod("Show2");
methodInfo1.Invoke(test, new object[] { 123 });
}
//重载方法调用
{
MethodInfo method=type.GetMethod("Show3",new Type[] { typeof(string),typeof(int)});
method.Invoke(test, new object[] { "现在是哪一年", 2020});
}
{
MethodInfo method = type.GetMethod("Show3",new Type[] { typeof(int),typeof(string)});
method.Invoke(test, new object[] { 2020, "year" });
}
{
MethodInfo method = type.GetMethod("Show3",new Type[] { typeof(int)});
method.Invoke(test, new object[] { 2020 });
}
{
MethodInfo method = type.GetMethod("Show3",new Type[] { typeof(string)});
method.Invoke(test, new object[] { "wang" });
}
{
MethodInfo method = type.GetMethod("Show3",new Type[] { });
method.Invoke(test, null);
}
//静态方法【特别】:实例可以要,也可以不要【null】
{
MethodInfo method = type.GetMethod("Show5");
method.Invoke(null,new object[] { "你好"});
}
{
MethodInfo method = type.GetMethod("Show5");
method.Invoke(test, new object[] { "你好" });
}
{//反射调用私有方法
var method = type.GetMethod("Show4", BindingFlags.Instance | BindingFlags.NonPublic);
method.Invoke(test, new object[] { "私有方法调用" });
}
}
那么泛型的方法又如何用反射调用呢?
{
Console.WriteLine("-----------------------------------扩展:泛型方法GenericMethod---------------------------------------------------");
Assembly assembly = Assembly.Load("DB.SqlServer");
Type type = assembly.GetType("DB.SqlServer.GenericMethod");
Object oGeneric = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod("Show");//泛型方法不用Show`3
var methodnew=method.MakeGenericMethod(new Type[]{ typeof(int),typeof(string), typeof(DateTime)});
methodnew.Invoke(oGeneric, new object[] { 12, "泛型方法 ", DateTime.Now });
}
泛型方法和泛型类同时存在的情况:
{
Console.WriteLine("扩展:泛型类+泛型方法GenericMethod");
Assembly assembly = Assembly.Load("DB.SqlServer");
Type type = assembly.GetType("DB.SqlServer.GenericDouble`1").MakeGenericType(new Type[] { typeof(int) });
Object oObject=Activator.CreateInstance(type);
MethodInfo method = type.GetMethod("Show").MakeGenericMethod(new Type[] {typeof(string),typeof(DateTime) });
method.Invoke(oObject,new object[] { 2020,"hello",DateTime.Today});
}
七:反射的应用:
//实体类不考虑扩展问题,没意义
Console.WriteLine("---------------------------------------------Reflection---------------------------------------------------------");
Type type = typeof(People);
Object opeople=Activator.CreateInstance(type);
//获取全部的属性
foreach (var prop in type.GetProperties())
{
Console.WriteLine($"{type.Name}.{prop.Name}={prop.GetValue(opeople)}");
//给prop里面的属性增加一个值
if (prop.Name.Equals("Id"))
{
prop.SetValue(opeople, 1);
}
else if(prop.Name.Equals("Name"))
{
prop.SetValue(opeople, "泛型实体属性");
}
Console.WriteLine($"{type.Name}.{prop.Name}={prop.GetValue(opeople)}");
}
foreach (var field in type.GetFields())
{
Console.WriteLine($"{type.Name}.{field.Name}={field.GetValue(opeople)}");
if (field.Name.Equals("Description"))
{
field.SetValue(opeople, "只是一个字段");
}
Console.WriteLine($"{type.Name}.{field.Name}={field.GetValue(opeople)}");
}
//思考:反射对实体层的操作如此麻烦,Why我们还要用它?eg:让我们一起看一个场景:
public Company FindOld(int Id)
{
string sql = @"SELECT [Id]
,[Name]
,[CreateTime]
,[CreatorId]
,[LastModifierId]
,[LastModifyTime]
FROM [Company]
where Id="+Id;
//[Name],[CreateTime]