反射
反射
反射反射程序员的快乐,反射是无处不在的,封装框架,系统开发,MVC,IOC,ORM都离不开反射。
一、反射/反编译工具/高级语言到计算机语言的历程
- 高级语言---编译---dll/exe文件
- metadata:元数据清单---记录了dll中包含哪些东西,是一个描述。
- IL:中间语言---编译把高级语言编译后得到c#中最真实的言语状态,面向对象语言。
- 反编译工具:逆向工程;ILSPY --- DLL/EXE、 --- 反编译回来,c#/IL
- 反射: 来自于System.Reflection,是一个帮助类库---可以读取DLL/EXE中metadata和使用metadata + 动态的创建dll/exe --- Emit技术
二、反射创建对象
首先定义一个接口
namespace Business.DB.Interface
{
/// <summary>
/// 数据访问类抽象
/// </summary>
public interface IDBHelper
{
void Query();
}
}
实现了接口的类
namespace Business.DB.SqlServer
{
public class SqlServerHelper
{
//Nuget:System.Data.SqlClient
private string ConnectionString = "Data Source=DESKTOP-VUL99EF; Database=CustomerDB; User ID=sa; Password=sa123; MultipleActiveResultSets=True";
private static string GetConnection()
{
//Nuget引入:
//SetBasePath:Microsoft.Extensions.Configuration.FileExtensions
//AddJsonFile:Microsoft.Extensions.Configuration.Json
var Configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true)
.Build();
return Configuration.GetConnectionString("Default");
}
public SqlServerHelper()
{
Console.WriteLine($"{this.GetType().Name}被构造");
}
//public SqlServerHelper(int i)
//{
// Console.WriteLine($"{this.GetType().Name}被构造");
//}
public void Query()
{
Console.WriteLine($"{this.GetType().Name}.Query");
}
}
}
我们开始尝试通过反射创建对象
//传统方式创建对象
IDBHelper dbHelper=new SqlServerHelper();
反射的方式
- 动态读取DLL
- LoadFrom: dll全名称,需要后缀
- LoadFile: 全路径,需要DLL后缀
- Load: dll名称,不需要后缀
- 获取某一个具体的类型:参数需要是类的全名称;
- 创建对象
- 类型转换
- 调用方法
Assembly assembly =Assembly.LoadFrom("Business.DB.SqlServer.dll");
//Assembly assembly1=Assembly.LoadFile(@"E:\VS项目\2022年vs项目\ReLearn\ReLearn\bin\Debug\net7.0\Business.DB.SqlServer.dll");
//Assembly assembly2=Assembly.Load("Business.DB.SqlServer");
Type type= assembly.GetType("Business.DB.SqlServer.SqlServerHelper");
object? oInstance= Activator.CreateInstance(type);
//oInstance.Query(); //报错。因为oInstance当做是一个object类型,object类型是没有Query方法的;
//C#语言是一个强类型语言:编译时你是什么类型,以左边为准;不能调用是因为编译器不允许;实际类型一定是SqlServerHelper类型;
//如果使用dynamic 作为类型的声明,在调用的时候,没有限制;
//dynamic:动态类型;不是编译时决定类型,避开编译器的检查;运行时决定是什么类型
//dynamic oInstance1= Activator.CreateInstance(type);
//oInstance1.Query();
//oInstance1.Get(); //报错了,因为SqlServerHelper没有Get方法。
SqlServerHelper sqlServerHelper = oInstance as SqlServerHelper;
sqlServerHelper.Query();
三、反射实现工厂,断开依赖
首先我们先使用传统的方式创建一个简单工厂
public class SimpleFactory
{
public static IDBHelper CreateInstance()
{
Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
Type type = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");
object? oInstance = Activator.CreateInstance(type);
IDBHelper helper = oInstance as IDBHelper;
return helper;
}
}
我们在使用的时候,只需要调用CreateInstance返回一个IDBHelper对象即可。
但是有时候可能会遇到产品经理,让我们使用不同数据库去存储我们的数据的情况,遇到这种情况的时候,可能就需要更改我们的代码来实现不同数据库的切换,因此我们就可以通过反射机制,尽可能的达到一劳永逸的结果。
改造一下我们的简单工厂模式
public class SimpleFactory
{
public static IDBHelper CreateInstance()
{
string ReflictionConfig = CustomConfigManager.GetConfig("ReflictionConfig");
string typeName = ReflictionConfig.Split(',')[0];
string dllName = ReflictionConfig.Split(',')[1];
Assembly assembly = Assembly.LoadFrom(dllName);
Type type = assembly.GetType(typeName);
object? oInstance = Activator.CreateInstance(type);
IDBHelper helper = oInstance as IDBHelper;
return helper;
}
}
public static class CustomConfigManager
{
public static string GetConfig(string key)
{
var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
IConfigurationRoot config = builder.Build();
string configValue = config.GetSection(key).Value;
return configValue;
}
}
创建SqlServerHelper的时候,没有出现SqlserverHelper;没有依赖SqlServerHelper;
依赖的是两个字符串Business.DB.SqlServer.dll +Business.DB.SqlServer.SqlServerHelper
去掉对细节的依赖的;依赖于抽象--不再依赖于细节;----依赖倒置原则; 增强代码的稳定性;
如果我们要切换到MySql服务器
- 按照接口实现一个Mysql帮助类库
- Copy Dll文件到执行目录下
- 修改配置文件
结果:不同功能的灵活切换---程序的配置;
如果一个新的功能还不存在,只需要把功能实现、dll Copy到执行目录下,修改配置文件---不需要停止程序的运行;
扩展一个新的功能进来;
两者的比较
- 传统方式,必须修改代码---必须要重新发布 编译 发布到服务器---步骤就很麻烦
- 反射实现:断开了对普通类的依赖;依赖于配置文件+接口(抽象)
四、反射黑科技--反射可以做到普通方式做不到的事情;反射破坏单例
先实现一个单例的代码:
public sealed class Singleton
{
private static Singleton instance;
private Singleton() { }
static Singleton()
{
instance = new Singleton();
}
public static Singleton GetInstance() {
return instance;
}
}
- 如果私有化修饰--除了反射以外,没有其他的方法来完成对私有化的访问的,私有化就只能从内部访问;
- 反射的强大之处---可以做到普通方法做不到的事儿;
- 反射动态记载dll,元数据中只要有的,都可以给找出来;
- 完全不用关注权限问题---为所欲为---小暗门。
创建一个单例以后,我们对比一下创建对象是否是一致的
Singleton singleton1 = Singleton.GetInstance();
Singleton singleton2 = Singleton.GetInstance();
Singleton singleton3 = Singleton.GetInstance();
Singleton singleton4 = Singleton.GetInstance();
Console.WriteLine(object.ReferenceEquals(singleton1, singleton2));
Console.WriteLine(object.ReferenceEquals(singleton2, singleton3));
Console.WriteLine(object.ReferenceEquals(singleton1, singleton4)
结果为
下面用反射我们来创建一下我们的对象,
Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
Type type = assembly.GetType("Business.DB.SqlServer.Singleton");
Singleton singleton1 = (Singleton)Activator.CreateInstance(type, true);
Singleton singleton2 = (Singleton)Activator.CreateInstance(type, true);
Singleton singleton3 = (Singleton)Activator.CreateInstance(type, true);
Singleton singleton4 = (Singleton)Activator.CreateInstance(type, true);
Console.WriteLine(object.ReferenceEquals(singleton1, singleton2));
Console.WriteLine(object.ReferenceEquals(singleton2, singleton3));
Console.WriteLine(object.ReferenceEquals(singleton1, singleton4));
这样就达到了通过反射破坏单例的现象。
五、反射调用方法+反射创建对象的升级篇
1.反射创建对象
在通过用反射创建对象的时候,如何调用类的带参数的构造函数那?下面我们演示一下,如何调用带参数的构造函数。
用来反射测试的类
/// <summary>
/// 反射测试类
/// </summary>
public class ReflectionTest
{
#region Identity
/// <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)
{
//typeof(int);
//Type //id.GetType();
Console.WriteLine($"这里是{this.GetType()} 有参数构造函数");
}
public ReflectionTest(string name,int id )
{
//typeof(int);
//Type //id.GetType();
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
}
反射的代码
//Type type = null;
//Activator.CreateInstance();//访问的是无参数构造函数创建对象;
//如何访问带参数的构造函数呢?
Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
Type type = assembly.GetType("Business.DB.SqlServer.ReflectionTest");
//a. 调用无参数构造函数的
object noParaObject = Activator.CreateInstance(type);
//b. 调用有参数的---需要传递一个object类型的数组作为参数,参数按照从昨往右匹配;严格匹配,按照参数的类型去执行对应的和参数类型匹配的构造函数,如果没有匹配的---报异常
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[] { "Richard", 456 });
2. 反射调用参数方法
反射调用方法一定要类型转换后才能调用吗? ---当然不是的;
- 获取方法MethodInfo
- 执行MethodInfo 的Invoke方法,传递方法所在的类的实例对象+参数
对于不同的类型的函数
a. 调用五参数的方法
Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
Type type = assembly.GetType("Business.DB.SqlServer.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);
b. 调用有参数的方法---重载方法--需要通过方法参数类型类区别方法,传递参数--严格匹配参数类型
Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
Type type = assembly.GetType("Business.DB.SqlServer.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);
c. 调用私有方法,在获取方法的时候,加上参数BindingFlags.NonPublic | BindingFlags.Instance
Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
Type type = assembly.GetType("Business.DB.SqlServer.ReflectionTest");
object oInstance = Activator.CreateInstance(type);
MethodInfo show4 = type.GetMethod("Show4", BindingFlags.NonPublic | BindingFlags.Instance);
show4.Invoke(oInstance, new object[] { "String" });
d. 调用泛型方法: 1. 获取到方法后,先确定类型,严格按照参数类型传递参数就可以正常调用
泛型是延迟声明,调用的时候,确定类型
{
//GenericMethod genericMethod = new GenericMethod();
//genericMethod.Show<string, int, DateTime>("", 23, DateTime.Now);
}
{
//Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
//Type type = assembly.GetType("Business.DB.SqlServer.GenericMethod");
//object oInstance = Activator.CreateInstance(type);
//MethodInfo show = type.GetMethod("Show");
////在执行之前选哟确定是什么类型;
//MethodInfo genericshow = show.MakeGenericMethod(new Type[] { typeof(int), typeof(string), typeof(Dat
//genericshow.Invoke(oInstance, new object[] { 123, "暖风昔人", DateTime.Now });
//genericshow.Invoke(oInstance, new object[] { "暖风昔人", 123, DateTime.Now });
////show.Invoke(); //到到底给什么类型呢? 泛型,其实不确定的类型的;
}
{
//Console.WriteLine(typeof(GenericClass<,,>));
//Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
//Type type = assembly.GetType("Business.DB.SqlServer.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 });
}
{
//Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
//Type type = assembly.GetType("Business.DB.SqlServer.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 });
}
六、反射多种应用场景
1.反射+ 配置文件+ 工厂-----后面要讲的IOC容器---IOC容器的雏形;简单版本的IOC容器;
2.调用方法--需要类型名称+ 方法名称就可以调用到方法 ---类类名称(字符串)+ 方法名称(字符串) ===就可以去调用方法--MVC框架
创建一个MVC ---http://localhost:64527/Home/Index 就可以调用到Index方法---就选需要创建Home的实例;执行Index方法---当然是反射啊;
七、 反射的局限性
- 有局限----性能问题
- 测试用例:普通方式循环100000次创建对象+方法调用:17 ms
反射方式循环100000此创建对象+方法调用:4544ms
代码
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();
//Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");//1 动态加载
//Type dbHelperType = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");//2 获取类型
for (int i = 0; i < 1000_000; i++)
{
Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");//1 动态加载
Type dbHelperType = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");//2 获取类型
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}");
}
}
运行的结果为:
- 第二次运行: 普通方式:15ms 反射方式:4517ms
确实有性能问题,主要损耗性能是加载DLL的时候损耗的,
怎么优化一下?
把动态加载Dll部分缓存起来了;每次直接使用加载的dll;
Stopwatch watch = new Stopwatch();
watch.Start();
Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");//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;
使用反射的建议:
- 反射确实有性能问题
- 需要理性对待,就不用吗?当然要用;因为反射的经过优化之后其实性能差也没有差多少的;因为反射能量确实是太大了,大家以后在工作中,可以放心使用;
八、反射调用字段
一个类的内部,除了构造函数、方法、属性、字段。
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 int Sex { get; set; }
public string Description; //字段
}
传统方式使用属性和字段
//传统方式使用属性和字段
{
Console.WriteLine("***********赋值*************");
People people = new People();
people.Id = 134;
people.Name = "陈大宝";
people.Age = 25;
people.Description = "高级班的VIP学员";
people.Sex = 1;
People people1 = new People()
{
Id = 134,
Name = "陈大宝",
Age = 25,
Description = "高级班的VIP学员"
};
Console.WriteLine("***********取值*************");
Console.WriteLine($"people.Id={people.Id}");
Console.WriteLine($"people.Name={people.Name}");
Console.WriteLine($"people.Age={people.Age}");
Console.WriteLine($"people.Sex={people.Sex}");
Console.WriteLine($"people.Description={people.Description}");
}
反射方式
Console.WriteLine("***********反射方式*************");
{
Type type = typeof(People);
object pObject = Activator.CreateInstance(type);
//pObject.Id=234;
foreach (var prop in type.GetProperties())
{
//Console.WriteLine(prop.Name);
//逐个属性赋值
if (prop.Name.Equals("Id"))
{
prop.SetValue(pObject, 134);
}
else if (prop.Name.Equals("Name"))
{
prop.SetValue(pObject, "陈大宝");
}
else if (prop.Name.Equals("Age"))
{
prop.SetValue(pObject, 25);
}
//else if (prop.Name.Equals("Age"))
//{
// prop.SetValue(pObject, 25);
//}
}
foreach (var prop in type.GetProperties())
{
Console.WriteLine($"people.{prop.Name}={prop.GetValue(pObject)}");
//if (prop.Name.Equals("Id"))
//{
// prop.GetValue(pObject);
//}
//else if (prop.Name.Equals("Name"))
//{
// prop.GetValue(pObject);
//}
//else if (prop.Name.Equals("Age"))
//{
// prop.GetValue(pObject);
// }
}
}
总结:
- 普通方法调用属性字段---简单快捷
- 反射操作---稍微麻烦点
- 让我们的程序更加稳定---
- 如果说People增加一个字段呢?增加一个属性呢?--普通方法就必须要修改代码---重新编译发布----不稳定
- 反射设置值:好像没啥优势
- 反射取值:不需要修改代码---代码就更加稳定
九、反射+ADO.NET时间数据库访问层---手写ORM框架
- ORM--对象关系映射
- 可以通过依赖于某个实体对象--做到对数据库中数据的操作
public class SqlServerHelper : IDBHelper
{
//Nuget:System.Data.SqlClient
private string ConnectionString = "Data Source=10.10.33.251; Database=ZhaoXiPracticeDB; User ID=sa; Password=7ujm&UJM; MultipleActiveResultSets=True";
private static string GetConnection()
{
//Nuget引入:
//SetBasePath:Microsoft.Extensions.Configuration.FileExtensions
//AddJsonFile:Microsoft.Extensions.Configuration.Json
var Configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true)
.Build();
return Configuration.GetConnectionString("Default");
}
public SqlServerHelper()
{
//Console.WriteLine($"{this.GetType().Name}被构造");
}
//public SqlServerHelper(int i)
//{
// Console.WriteLine($"{this.GetType().Name}被构造");
//}
public void Query()
{
//Console.WriteLine($"{this.GetType().Name}.Query");
}
/// <summary>
/// 查询Company
/// 条件:
/// 1.必须有一个Id字段--继承BaseModel
/// 2.要求实体对象中的属性结构必须和数据完全一致
/// </summary>
public SysCompany QuerySysCompany(int id)
{
//开始ORM
//1.链接数据---数据库链接字符串ConnectionString
//2.准备SqlConnection,使用数据库链接字符串
//SysCompany result = new SysCompany();
//7.反射创建对象oReulst--- 给oReulst 赋值然后返回 oReulst
Type type = typeof(SysCompany);
object oReulst = Activator.CreateInstance(type);
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
connection.Open();//打开数据库链接
//3.准时Sql语句
#region MyRegion
string sql = @"SELECT [Id]
,[Name]
,[CreateTime]
,[CreatorId]
,[LastModifierId]
,[LastModifyTime]
FROM [ZhaoXiPracticeDB].[dbo].[SysCompany] where id=" + id;
#endregion
//4.准备SqlCommand
SqlCommand sqlCommand = new SqlCommand(sql, connection);
//5.通过SqlCommand对象执行Sql语句
SqlDataReader reader = sqlCommand.ExecuteReader();
//6.开始获取数据
if (reader.Read())
{
//object oId = reader["Id"];
//result.Id = Convert.ToInt32(oId);//不行;
//object oName = reader["Name"];
//8.反射赋值?
foreach (var prop in type.GetProperties())
{
//if (prop.Name.Equals("Id"))
//{
// prop.SetValue(oReulst, reader["Id"]); //Id
//}
//else if (prop.Name.Equals("Name"))
//{
// prop.SetValue(oReulst, reader[prop.Name]);
//}
//else if (prop.Name.Equals("CreateTime"))
//{
// prop.SetValue(oReulst, reader[prop.Name);
//}
//else if (true)
//{
//.......
//}
Console.WriteLine(prop.GetValue(oReulst, null));
// Console.WriteLine(prop.Name);
}
}
//问题来了,数据可以查询到---需要返回一个对象--需要赋值给对象,然后返回对象;----反射
}
return (SysCompany)oReulst;
}
}
十、Emit技术:在运行时去动态的生成Dll、Exe包括dll内部的方法、属性、字段
- 动态--;可以在代码运行的时候去动态的定义业务逻辑;
- 偏向于底层--学习成本比较高 除非是非常复杂的业务逻辑,一般情况,用的比较少---动态代理的时候,还会再讲的;
ReflectionEmit.cs
public class ReflectionEmit
{
public static void Show()
{
// AssemblyBuilder// 建造者模式---创建Assembly
//AssemblyName assemblyName = new AssemblyName("DynamicAssemblyExample");
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("DynamicAssemblyExample"), AssemblyBuilderAccess.RunAndCollect);
// 对于单个模块程序集,模块名称通常为;程序集名称加上扩展名。
ModuleBuilder modulebuilder = assemblyBuilder.DefineDynamicModule("MyModal"); //托管模块
TypeBuilder typebuilder = modulebuilder.DefineType("MyDynamicType", TypeAttributes.Public);
//Type type= typebuilder.CreateType();
// 在Type中生成私有字段
FieldBuilder fieldBuilder = typebuilder.DefineField("NumberField", typeof(int), FieldAttributes.Public);
// 定义一个接受整数参数的构造函数,储存在私人区域。
Type[] parameterTypes = { typeof(int) };
ConstructorBuilder ctor1 = typebuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, parameterTypes);
//中间语言的生成者
ILGenerator ctor1IL = ctor1.GetILGenerator();
//对于构造函数,参数0是对新
//实例。在调用base之前将其推到堆栈上
//类构造函数。指定的默认构造函数
//通过传递
//类型(Type.EmptyTypes)到GetConstructor。
ctor1IL.Emit(OpCodes.Ldarg_0);
ctor1IL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
//在推送参数之前,先将实例推送到堆栈上
//将被分配给私有字段m\u编号。
ctor1IL.Emit(OpCodes.Ldarg_0);
ctor1IL.Emit(OpCodes.Ldarg_1);
ctor1IL.Emit(OpCodes.Stfld, fieldBuilder);
ctor1IL.Emit(OpCodes.Ret);
//完成构造函数传值, Int 设置给字段--测试代码
{
//MyDynamicType myDynamicType = new MyDynamicType(123456);
//int number = myDynamicType.NumberField;
//// 动态的生成程序集;
//// 动态的生成类;
//// 动态的生成字段;
//// 动态的生成构造函数;
//Type type1 = typebuilder.CreateType();
//object oInstacne = Activator.CreateInstance(type1, new object[] { 123456 });
//FieldInfo fieldInfo = type1.GetField("NumberField");
//object numberFieldResult = fieldInfo.GetValue(oInstacne);
}
MethodBuilder consoleMethod = typebuilder.DefineMethod("ConsoleMethod", MethodAttributes.Public | MethodAttributes.Static, null, null);
ILGenerator consoleMethodIL = consoleMethod.GetILGenerator();
consoleMethodIL.Emit(OpCodes.Ldstr, "欢迎来到高级班第15期进阶学习");
consoleMethodIL.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
consoleMethodIL.Emit(OpCodes.Ret); //写IL最后一定要Ret
{
Console.WriteLine("测试。。。。。。");
Type type1 = typebuilder.CreateType();
object oInstacne = Activator.CreateInstance(type1, new object[] { 123456 });
MethodInfo myMethod = type1.GetMethod("ConsoleMethod");
object oResult = myMethod.Invoke(oInstacne, null);
}
MethodBuilder AddMethod = typebuilder.DefineMethod("AddMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[] { typeof(int), typeof(int) });
ILGenerator AddMethodIL = AddMethod.GetILGenerator();
AddMethodIL.Emit(OpCodes.Ldarg_0);
AddMethodIL.Emit(OpCodes.Ldarg_1);
AddMethodIL.Emit(OpCodes.Add_Ovf_Un);
AddMethodIL.Emit(OpCodes.Ret);
{
//Console.WriteLine("测试。。。。。。");
//Type type1 = typebuilder.CreateType();
//object oInstacne = Activator.CreateInstance(type1, new object[] { 123456 });
//MethodInfo myMethod = type1.GetMethod("AddMethod");
//object oResult = myMethod.Invoke(oInstacne, new object[] { 12, 34 });
}
}
}
main.cs
Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
Module[] modules = assembly.GetModules();
foreach (var module in modules)
{
Console.WriteLine(module.Name);
foreach (var type in module.GetTypes())
{
Console.WriteLine(type.Name);
}
}
ReflectionEmit.Show();