首页 > 编程语言 >C#高级--反射详解

C#高级--反射详解

时间:2023-04-06 16:56:23浏览次数:57  
标签:Console C# object dll -- 详解 WriteLine type public

原文:C#高级--反射详解_c# 反射_李宥小哥的博客-CSDN博客

C#高级–反射详解
零、文章目录
一、反射是什么
1、C#编译运行过程
高级语言->编译->dll/exe文件->CLR/JIT->机器码

 

2、原理解析
metadata:元数据数据清单,记录了dll中包含了哪些东西,是一个描述。
IL:中间语言,编译把高级语言编译后得到的C#中最真实的语言状态,面向对象语言。

反射:来自于System.Reflection,是一个帮助类库,可以读取dll/exe中metadata,使用metadata创建对象。

Emit:一种反射技术,可以动态创建dll/exe。

反编译工具:ILSpy可以反编译dll/exe,查看对应的C#/IL代码。

二、反射创建对象
1、动态读取dll
LoadFrom:dll全名称,需要后缀
LoadFile:全路径,需要dll后缀
Load:dll名称不需要后缀
//1、动态读取dll的三种方式
//(1)LoadFrom:dll全名称,需要后缀
Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
//(2)LoadFile:全路径,需要dll后缀
//Assembly assembly1 = Assembly.LoadFile(@"dll文件全路径");
//(3)Load:dll名称 不需要后缀
//Assembly assembly2 = Assembly.Load("Business.DB.SqlServer");
1
2
3
4
5
6
7
2、获取类型
//2、获取某一个具体的类型,参数需要是类的全名称
Type type1 = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");
1
2
3、创建对象
直接传类型
重载方法,传dll的全名称
返回值是object类型,不能直接调用方法
//3、创建对象
//(1)直接传类型
object? oInstance = Activator.CreateInstance(type1);
//(2)重载方法,传dll的全名称
//object? oInstanc1= Activator.CreateInstance("Business.DB.SqlServer.dll", "Business.DB.SqlServer.SqlServerHelper");
//a.oInstance.Query();//报错了:因为oInstance当做是一个object类型,object类型是没有Query方法;C#语言是一种强类型语言;编译时决定你是什么类型,以左边为准;不能调用是因为编译器不允许;实际类型一定是SqlServerHelper;
//b.如果使用dynamic 作为类型的声明,在调用的时候,没有限制;
//c.dynamic :动态类型:不是编译时决定类型,避开编译器的检查;运行时决定是什么类型
//d.dynamic dInstance = Activator.CreateInstance(type);
//e.dInstance.Query();
//f.dInstance.Get(); //报错了--因为SqlServerHelper没有Get方法
1
2
3
4
5
6
7
8
9
10
11
4、类型转换
//4、类型转换
// SqlServerHelper helper = (SqlServerHelper)oInstance; //不建议这样转换--如果真实类型不一致--会报报错;
IDBHelper helper = oInstance as IDBHelper;//如果类型一直,就转换,如果不一致;就返回null
1
2
3
5、调用方法
//5、调用方法
helper.Query();
1
2
三、反射创建对象封装
问题:反射创建对象代码很多。

其实除了dll的名称和类的全名称都是一样的代码,可以封装反射创建对象帮助类。
传入的参数都是字符串,可做成配置项,提高代码扩展性和灵活性,可以做到不修改代码而改变程序的功能。
1、反射创建对象封装
(1)创建SqlServerHelper的时候,没有依赖SqlServerHelper

(2)依赖的是两个字符串Business.DB.SqlServer.dll + Business.DB.SqlServer.SqlServerHelper,从配置文件读取。

(3)去掉个对细节的依赖的:依赖于抽象,不再依赖于细节;依赖倒置原则; 增强代码的稳定性;

using Business.DB.Interface;
using Microsoft.Extensions.Configuration;
using System;
using System.Reflection;

namespace MyReflecttion
{
public class SimpleFactory
{
//创建SqlServerHelper的时候,没有依赖SqlServerHelper
//依赖的是两个字符串Business.DB.SqlServer.dll + Business.DB.SqlServer.SqlServerHelper
//去掉个对细节的依赖的:依赖于抽象,不再依赖于细节;依赖倒置原则; 增强代码的稳定性;
public static IDBHelper CreateInstance()
{
string ReflictionConfig = CustomConfigManager.GetConfig("ReflictionConfig");
//Business.DB.SqlServer.SqlServerHelper,Business.DB.SqlServer.dll
string typeName = ReflictionConfig.Split(',')[0];
string dllName = ReflictionConfig.Split(',')[1];

//Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
//Type type = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");

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
{
//Core 读取配置文件:appsettings
//1.Microsoft.Extensions.Configuration;
//2.Microsoft.Extensions.Configuration.Json
public static string GetConfig(string key)
{
var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json"); //默认读取 当前运行目录
IConfigurationRoot configuration = builder.Build();
string configValue = configuration.GetSection(key).Value;
return configValue;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
2、配置文件
"ReflictionConfig": "Business.DB.Orcale.OrcaleHelper,Business.DB.Orcale.dll"
1
3、程序调用
如果公司来了一个新的技术经理:要将SqlServer换成MySql
(1)传统方式,必须要修改代码,然后必须要重新编译发布,步骤很多

(2)反射实现:断开了对普通类的依赖;依赖于配置文件+接口(抽象),做到了程序的可配置

(3)反射实现的步骤:按照接口约定实现一个Mysql帮助类库,Copy dll 文件到执行目录下,修改配置文件

IDBHelper helper1 = SimpleFactory.CreateInstance();
helper1.Query();
1
2
四、反射创建对象之破环单例
除了反射之外,没有其他的方法来调用私有化构造函数的;私有化的方法就只能从内部访问;元数据中只要有的,反射都可以给找出来; 完全不用关注权限问题;

1、单例类代码
using System;

namespace MyReflecttion
{
/// <summary>
/// 单例模式:类,能保证在整个进程中只有一个实例
/// </summary>
public sealed class Singleton
{
private static Singleton _Singleton = null;
/// <summary>
/// 创建对象的时候执行
/// </summary>
private Singleton()
{
Console.WriteLine("Singleton被构造");
}

/// <summary>
/// 被CLR 调用 整个进程中 执行且只执行一次
/// </summary>
static Singleton()
{
_Singleton = new Singleton();
}

public static Singleton GetInstance()
{
return _Singleton;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2、传统手艺创建单例类对象
//1、传统手艺创建单例类对象
Console.WriteLine("********************传统单例***************************");
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));
Console.WriteLine("********************传统单例***************************");
1
2
3
4
5
6
7
8
9
10
3、反射方式创建单例类对象
//2、反射方式创建单例类对象
Console.WriteLine("********************反射单例***************************");
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.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));
Console.WriteLine("********************反射单例***************************");
1
2
3
4
5
6
7
8
9
10
11
12
4、运行结果
********************传统单例***************************
Singleton被构造
True
True
True
********************传统单例***************************
********************反射单例***************************
Singleton被构造
Singleton被构造
Singleton被构造
Singleton被构造
False
False
False
********************反射单例***************************
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
五、反射创建对象详解
1、创建测试类
普通类重载方法

using System;

namespace MyReflecttion
{
/// <summary>
/// 反射测试类
/// </summary>
public class ReflectionTest
{
#region Actor
/// <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)
{
Console.WriteLine($"这里是{this.GetType()} 有参数构造函数");
}

public ReflectionTest(string name,int id )
{
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
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
泛型方法泛型类

using System;

namespace MyReflecttion
{

public class GenericMethod
{
public void Show<T, W, X>(T t, W w, X x)
{
Console.WriteLine($"t.type={t.GetType().Name},w.type={ w.GetType().Name},x.type={x.GetType().Name}");
}
}


public class GenericClass<T, W, X>
{
public void Show(T t, W w, X x)
{
Console.WriteLine($"t.type={t.GetType().Name},w.type={w.GetType().Name},x.type={x.GetType().Name}");
}
}

public class GenericDouble<T>
{
public void Show<W, X>(T t, W w, X x)
{
Console.WriteLine($"t.type={t.GetType().Name},w.type={w.GetType().Name},x.type={x.GetType().Name}");
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2、创建对象
(1)调用无参数构造函数的
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.ReflectionTest");
object noParaObject = Activator.CreateInstance(type);
1
2
3
(2)调用有参数构造函数的
需要传递一个object类型的数组作为参数,参数按照从昨往右严格匹配,如果没有匹配的报异常

Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.ReflectionTest");
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[] { "五五", 456 });
1
2
3
4
5
6
六、反射调用方法详解
获取方法MethodInfo,执行MethodInfo 的Invoke方法,传递方法所在的类的实例对象+参数

1、调用无参数的方法
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.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);
1
2
3
4
5
6
7
2、调用有参数的方法
需要通过方法参数类型类区别方法,传递参数,严格匹配参数类型

Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.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);
1
2
3
4
5
6
7
8
9
10
11
12
13
3、调用私有方法
在获取方法的时候,加上参数BindingFlags.NonPublic | BindingFlags.Instance

Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.ReflectionTest");
object oInstance = Activator.CreateInstance(type);
MethodInfo show4 = type.GetMethod("Show4", BindingFlags.NonPublic | BindingFlags.Instance);
show4.Invoke(oInstance, new object[] { "String" });
1
2
3
4
5
4、调用静态方法
不需要创建对象也可以调用

Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.ReflectionTest");
MethodInfo show5 = type.GetMethod("Show5");
show5.Invoke(null, new object[] { "String" });
1
2
3
4
5、调用普通类的泛型方法
获取到泛型方法后需要先确定类型

Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.GenericMethod");
object oInstance = Activator.CreateInstance(type);
MethodInfo show = type.GetMethod("Show");
//获取到泛型方法后需要先确定类型
MethodInfo genericshow = show.MakeGenericMethod(new Type[] { typeof(int), typeof(string), typeof(DateTime) });
genericshow.Invoke(oInstance, new object[] { 123, "三三三", DateTime.Now });
1
2
3
4
5
6
7
6、调用泛型类的普通方法
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
//泛型类的类型需要在类后面加占位符
Type type = assembly.GetType("MyReflecttion.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 });
1
2
3
4
5
6
7
8
7、调用泛型类的泛型方法
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
//泛型类的类型需要在类后面加占位符
Type type = assembly.GetType("MyReflecttion.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
2
3
4
5
6
7
8
9
10
七、反射操作属性字段
普通方法调用属性字段简单快捷,反射操作麻烦点。
类增加一个字段呢,普通方法调用需要修改代码,重新编译发布,代码不稳定,反射赋值没啥优势,反射取值不需要修改代码,代码就更加稳定。
type.GetProperties()获取属性,type.GetFields()获取字段。
1、创建测试类
using System;

namespace Business.DB.Model
{
/// <summary>
/// 实体---属性是不能保存数据,只有字段才能保存数据
/// </summary>
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 string Description;//字段
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2、传统方式赋值取值
//传统手艺赋值取值
Console.WriteLine("***********传统手艺赋值取值*************");
People people = new People();
people.Id = 134;
people.Name = "WWWW";
people.Age = 25;
people.Description = "XXXXX";
Console.WriteLine($"people.Id={people.Id}");
Console.WriteLine($"people.Name={people.Name}");
Console.WriteLine($"people.Age={people.Age}");
Console.WriteLine($"people.Description={people.Description}");
1
2
3
4
5
6
7
8
9
10
11
3、反射方式赋值取值
//反射方式赋值取值
Console.WriteLine("***********反射方式赋值取值*************");
Type type = typeof(People);
object pObject = Activator.CreateInstance(type);
foreach (var prop in type.GetProperties())
{
if (prop.Name.Equals("Id"))
{
prop.SetValue(pObject, 134);
}
else if (prop.Name.Equals("Name"))
{
prop.SetValue(pObject, "WWWW");
}
else if (prop.Name.Equals("Age"))
{
prop.SetValue(pObject, 25);
}
}
foreach (var prop in type.GetProperties())
{
Console.WriteLine($"people.{prop.Name}={prop.GetValue(pObject)}");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
4、运行结果
***********传统手艺赋值取值*************
Business.DB.Model.People被创建
people.Id=134
people.Name=WWWW
people.Age=25
people.Description=XXXX
***********反射方式赋值取值*************
Business.DB.Model.People被创建
people.Id=134
people.Name=WWWW
people.Age=25
1
2
3
4
5
6
7
8
9
10
11
八、反射的局限/性能问题
1、性能对比代码实现
using Business.DB.Interface;
using Business.DB.SqlServer;
using System;
using System.Diagnostics;
using System.Reflection;

namespace MyReflecttion
{
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();
//优化代码,加载dll放到循环外面
Assembly assembly = Assembly.Load("Business.DB.SqlServer");//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;
}

Console.WriteLine($"commonTime={commonTime} reflectionTime={reflectionTime}");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2、运行结果
测试用例:普通方式循环100000次,创建对象+方法调用:16毫秒
反射方式循环100000次,加载dll+创建对象+方法调用:6300毫秒
加载dll放到循环外面,创建对象+方法调用放循环里面,泛型方法:57毫秒
*******************Monitor*******************
commonTime=16 reflectionTime=57
1
2
3、使用反射的建议
反射确实有性能问题,但是差别没有那么大,在需要的地方可以放心使用

九、反射实现ORM框架
ORM:对象关系映射,通过对象查询数据库数据

1、创建测试类,继承基类
using System;

namespace Business.DB.Model
{
public class SysCompany : BaseModel
{
public string Name { get; set; }
public DateTime CreateTime { get; set; }
public int CreatorId { get; set; }
public int? LastModifierId { get; set; }
public DateTime? LastModifyTime { get; set; }
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
namespace Business.DB.Model
{
/// <summary>
/// 数据库basemodel
/// </summary>
public class BaseModel
{
public int Id { set; get; }
}
}
1
2
3
4
5
6
7
8
9
10
2、使用泛型方法来兼容不同的对象查询
3、使用反射获取字段
4、使用反射来对结果赋值
using Business.DB.Interface;
using Business.DB.Model;
using System;
using System.Data.SqlClient;

namespace Business.DB.SqlServer
{
public class SqlServerHelper : IDBHelper
{

//Nuget:System.Data.SqlClient

private string ConnectionString = "Data Source=XXX; Database=YYY; User ID=sa; Password=ZZZ; MultipleActiveResultSets=True";

public SqlServerHelper()
{
//Console.WriteLine($"{this.GetType().Name}被构造");
}

public void Query()
{
//Console.WriteLine($"{this.GetType().Name}.Query");
}

/// <summary>
/// 泛型方法适配查询不同对象数据
/// </summary>
public T Find<T>(int id) where T : BaseModel
{
//(1)反射创建对象
Type type = typeof(T);
object oReulst = Activator.CreateInstance(type);

//(2)连接数据库,数据库链接字符串ConnectionString
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
//(3)准备SqlConnection,使用数据库链接字符串
connection.Open();

//(4)准备sql,通过泛型缓存缓存sql
string sql = ConstantSqlString<T>.GetFindSql(id);
//(5)准备SqlCommand
//(6)通过SqlCommand对象执行Sql语句
SqlCommand sqlCommand = new SqlCommand(sql, connection);
//(7)开始获取数据
SqlDataReader reader = sqlCommand.ExecuteReader();
if (reader.Read())
{
foreach (var prop in type.GetProperties())
{
//(8)反射赋值
prop.SetValue(oReulst, reader[prop.Name] is DBNull ? null : reader[prop.Name]);
}
}
}
return (T)oReulst;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
5、使用泛型缓存来缓存sql
using System;
using System.Linq;

namespace Business.DB.SqlServer
{
public class ConstantSqlString<T>
{
private static string FindSql = null;

static ConstantSqlString()
{
Type type = typeof(T);
FindSql = $"Select {string.Join(',', type.GetProperties().Select(c => $"[{c.Name}]").ToList())} from {type.Name} where id=";
}

/// <summary>
/// GetSql语句
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static string GetFindSql(int id)
{
return $"{FindSql}{id}";
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
6、反射多种应用场景
(1)IOC容器:反射+ 配置文件+ 工厂
(2)MVC框架:反射调用类型方法
(3)ORM:反射+泛型+Ado.Net
(4)AOP:在方法的前面后面添加处理内容
十、反射的Emit的技术
在运行时去动态的生成dll、exe包括dll内部的方法、属性、字段等内容
偏向于底层,学习成本比较高 除非是非常复杂的业务逻辑,一般情况,用的比较少;
1、程序集内部结构


2、动态创建程序过程
一般的,使用反射发出(reflection emit)可能会是这样子的步骤

(1)创建一个新的程序集
程序集是动态的存在于内存中或把它们保存到磁盘上

(2)在程序集内部,创建一个模块
(3)在模块内部,创建一个类型
(4)给类型添加属性和方法
(5)产生属性和方法内部的代码
3、参照类
public class MyDynamicType
{
/// <summary>
/// 字段
/// </summary>
public int NumberField = 0;

/// <summary>
/// int类型参数构造函数
/// </summary>
/// <param name="numberField"></param>
public MyDynamicType(int numberField)
{
this.NumberField = numberField;
}

/// <summary>
/// 无参数方法
/// </summary>
public void ConsoleMethod()
{
Console.WriteLine("方法输出的内容");
}

/// <summary>
/// 有参数方法
/// </summary>
/// <param name="para"></param>
/// <returns></returns>
public int MyMethod(int para)
{
return 2 * para;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
4、emit实现参照类
public class ReflectionEmit
{
//一般的,使用反射发出(reflection emit)可能会是这样子的步骤
//(1)创建一个新的程序集
//程序集是动态的存在于内存中或把它们保存到磁盘上
//(2)在程序集内部,创建一个模块
//(3)在模块内部,创建一个类型
//(4)给类型添加属性和方法
//(5)产生属性和方法内部的代码
public static void Show()
{

//(1)创建一个新的程序集
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("DynamicAssemblyExample"), AssemblyBuilderAccess.RunAndCollect);

//(2)在程序集内部,创建一个模块
ModuleBuilder modulebuilder = assemblyBuilder.DefineDynamicModule("MyModal");

//(3)在模块内部,创建一个类型
TypeBuilder typebuilder = modulebuilder.DefineType("MyDynamicType", TypeAttributes.Public);

//(4)给类型添加属性和方法
// 在Type中生成字段
FieldBuilder fieldBuilder = typebuilder.DefineField("NumberField", typeof(int), FieldAttributes.Public);

#region 定义一个接受整数参数的构造函数
//(4)给类型添加属性和方法
Type[] parameterTypes = { typeof(int) };
ConstructorBuilder ctor1 = typebuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, parameterTypes);

//(5)产生属性和方法内部的代码
//中间语言的生成者
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);//写IL最后一定要Ret

//测试代码
{
//Type type1 = typebuilder.CreateType();
//object oInstacne = Activator.CreateInstance(type1, new object[] { 123456 });
//FieldInfo fieldInfo = type1.GetField("NumberField");
//object numberFieldResult = fieldInfo.GetValue(oInstacne);
}
#endregion

#region 定义一个无参数方法
//(4)给类型添加属性和方法
MethodBuilder consoleMethod = typebuilder.DefineMethod("ConsoleMethod", MethodAttributes.Public | MethodAttributes.Static, null, null);

//(5)产生属性和方法内部的代码
ILGenerator consoleMethodIL = consoleMethod.GetILGenerator();
consoleMethodIL.Emit(OpCodes.Ldstr, "方法输出的内容");
consoleMethodIL.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
consoleMethodIL.Emit(OpCodes.Ret);

//测试代码
{
//Type type1 = typebuilder.CreateType();
//object oInstacne = Activator.CreateInstance(type1, new object[] { 123456 });
//MethodInfo myMethod = type1.GetMethod("ConsoleMethod");
//object oResult = myMethod.Invoke(oInstacne, null);
}
#endregion

#region 定义一个有参数方法
//(4)给类型添加属性和方法
MethodBuilder AddMethod = typebuilder.DefineMethod("MyMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[] { typeof(int), typeof(int) });

//(5)产生属性和方法内部的代码
ILGenerator AddMethodIL = AddMethod.GetILGenerator();
AddMethodIL.Emit(OpCodes.Ldarg_0);
AddMethodIL.Emit(OpCodes.Ldarg_1);
AddMethodIL.Emit(OpCodes.Add_Ovf_Un);
AddMethodIL.Emit(OpCodes.Ret);

//测试代码
{
//Type type1 = typebuilder.CreateType();
//object oInstacne = Activator.CreateInstance(type1, new object[] { 123456 });
//MethodInfo myMethod = type1.GetMethod("MyMethod");
//object oResult = myMethod.Invoke(oInstacne, new object[] { 12, 34 });
}
#endregion
}
}
————————————————
版权声明:本文为CSDN博主「李宥小哥」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/liyou123456789/article/details/119548050

标签:Console,C#,object,dll,--,详解,WriteLine,type,public
From: https://www.cnblogs.com/bruce1992/p/17293318.html

相关文章

  • AI智慧工地视频融合平台EasyCVR视频调阅模块一键播放功能优化
    EasyCVR可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等,以及厂家私有协议与SDK接入,包括海康Ehome、海康SDK、大华SDK等(具体见下图),能对外分发RTSP、RTMP、FLV、HLS、WebRTC等格式的视频流,在视频能力上,具备视频实时监控、视频录像、云存储、......
  • Go 语言进行依赖包的管理的几种方式
    Go语言具有丰富的开源包管理工具,可以方便地进行依赖包的管理。下面介绍如何在Go语言中安装依赖包。使用goget命令goget命令可以从远程代码仓库(比如GitHub)下载Go语言包,并安装到$GOPATH/src目录下。通常使用以下命令来安装一个依赖包:gogetgithub.com/依赖库路径......
  • python中动态导入文件的方法
    1.简介在实际项目中,我们可能需要在执行代码的过程中动态导入包并执行包中的相应内容,通常情况下,我们可能会将所需导入的包及对象以字符串的形式传入,例如test.test.run,下面将介绍如何动态导入。假设存在如下包:其中test.py的内容如下:count=1defrun():print("run")......
  • SQL语句的其他关键字
    目录数据准备编写SQL语句小技巧查询关键字之where筛选查询关键字之groupby分组查询关键字之having过滤查询关键字之distinct去重查询关键字之orderby排序查询关键字之limit分页查询关键字之regexp正则表达式多表查询的思路数据准备#数据准备createtableemp(idintpri......
  • 痞子衡嵌入式:利用i.MXRT1xxx系列ROM集成的DCD功能可轻松配置指定外设
    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是利用i.MXRT1xxx系列ROM集成的DCD功能可轻松配置指定外设。关于i.MXRT1xxx系列芯片BootROM中集成的DCD功能这个话题,痞子衡早就想写了,但是一直没有动笔,毕竟这个话题比较生涩,单独讲会比较枯燥。最近痞......
  • super关键字
    super关键字基本介绍super代表父类的引用,可以引用方法,属性以及构造器基本语法访问父类的属性,但不能访问private属性,案例:super.属性名;访问父类的方法,但不能访问private方法,案例:super.方法名(参数列表);访问父类的构造器,super.(参数列表);super带来的便利/细节调用父类构......
  • 宿命大乱斗
    宿命大乱斗是一款由台湾hobbyark开发的moba手游。游戏中你将扮演不同颜色的角色,组队与玩家对战。深入的策略性,无尽的装备选择和多种属于自己的英雄形象,让宿命大乱斗成为了一款独一无二的手游。《宿命大乱斗》游戏亮点:游戏的画面十分的精致。制作组花费了大量的时间设计每个角色......
  • vue.config.js 配置
    const{defineConfig}=require('@vue/cli-service')  //按需引入  //constAutoImport=require('');constAutoImport=require('unplugin-auto-import/webpack')constComponents=require('unplugin-vue-components/webpac......
  • Mac应用程序无法打开提示不明开发者或文件损坏的处理方法
     在安装Mac电脑应用程序的时候,经常会遇到“xxx.app已损坏,打不开。您应该将它移到废纸篓“或”打不开的xxx.app,因为它来自身份不明的开发者”,如图:   遇到上述情况是不是真的要移动到废纸篓呢?下面小编就为您带来Mac应用程序无法打开提示不明开发者或文件损坏的处理方法,解......
  • Spark on Yarn配置
    1、SparkonYarn配置1.)在搭建好的Spark上修改spark-env.sh文件:#vim$SPARK_HOME/conf/spark-env.sh添加以下配置:exportHADOOP_CONF_DIR=${HADOOP_HOME}/etc/hadoopexportYARN_CONF_DIR=${HADOOP_HOME}/etc/hadoop2.)在搭建好的Spark上修改spark-defaults.conf文......