BigBox777

爱学习、爱运动

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::

阅读目录

1、介绍

2、实现

  2.1、非泛型反射

  2.3、泛型反射

3、扩展

  3.1、通过配置修改数据库类型

 4、参考

返回系列文章目录 

 

//==================================实现反射注入================================
var protocolOmronFinsPath = @"C:\Users\wangmin\Desktop\ITL.Mom.Scada.WebAPI\ITL.Mom.Scada.Protocol.OmronFins\bin\Debug\net6.0\ITL.Mom.Scada.Protocol.OmronFins.dll";
var protocolOPCUAClientPath = @"C:\Users\wangmin\Desktop\ITL.Mom.Scada.WebAPI\ITL.Mom.Scada.Protocol.OPCUAClient\bin\Debug\net6.0\ITL.Mom.Scada.Protocol.OPCUAClient.dll";
var protocolOmronFinsAssembly = Assembly.LoadFrom(protocolOmronFinsPath).GetTypes().Where(t=>t.GetInterfaces().Any(n=>n.Name== "IDriverClient"));
var protocolOPCUAClientAssembly = Assembly.LoadFrom(protocolOPCUAClientPath).GetTypes().Where(t => t.GetInterfaces().Any(n => n.Name == "IDriverClient"));
services.AddScoped(typeof(IDriverClient), protocolOmronFinsAssembly.GetType());
services.AddScoped(typeof(IDriverClient), protocolOPCUAClientAssembly.GetType());

  

开始阅读  完整代码下载

1、介绍

   反射提供描述程序集、模块和类型的对象(Type 类型)。 可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型,然后调用其方法或访问其字段和属性。 如果代码中使用了特性,可以利用反射来访问它们。大白话说就是,可以把编译成exe或者dll的程序集,加载到内存创建对象来使用,后面会扩展一个案例,程序集里面通过配置切换多个数据库使用。

2、实现

2.1、非泛型反射

  做实现之前需要准备一个dll文件,我这里用的net5编译的,下面给出源代码,编译好后,另外创建一个项目来实现非泛型反射的实现。
注意:Assembly.LoadFile只载入相应的dll文件,比如Assembly.LoadFile("a.dll"),则载入a.dll,假如a.dll中引用了b.dll的话,b.dll并不会被载入。Assembly.LoadFrom则不一样,它会载入dll文件及其引用的其他dll,比如上面的例子,b.dll也会被载入。

 1 // 
 2 using System;
 3 
 4 namespace Demo02_DLL
 5 {
 6     [Author("BigBox777")]
 7     public class Test
 8     {
 9         public int Id=2;
10         public string Name { get; set; }
11         public DateTime Time { get; set; }
12         public Test()
13         {
14             Name = "BigBox777";
15             Time = DateTime.Parse("1971-01-01 12:00:00");
16             Console.WriteLine("调用了构造函数:public Test()");
17         }
18         public Test(string name)
19         {
20             Name = name;
21             Time = DateTime.Parse("1971-01-01 12:00:00");
22             Console.WriteLine($"调用了构造函数:public Test(string name),传入参数:{name}");
23         }
24         public void Show()
25         {
26             Console.WriteLine($"Id:{Id},Name:{Name},Time:{Time}");
27         }
28     }
29     //自定义特性
30     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
31     public class Author : System.Attribute
32     {
33         string name; 
34         public double version;
35 
36         public Author(string name)
37         {
38             this.name = name;
39 
40             version = 1.0;
41         }
42 
43         public string GetName()
44         {
45             return name;
46         }
47     }
48 }
Net5生成测试用的dll

 

 1 // 载入dll枚举出类型和模块
 2 string dllPath = @"D:\学习\CSharp技术栈入门到精通系列\Demo02-DLL\bin\Debug\net5.0\Demo02-DLL.dll";
 3 Assembly assembly = Assembly.LoadFile(dllPath);
 4 Console.WriteLine("------------枚举出类型------------");
 5 foreach (var typeItem in assembly.GetTypes())
 6 {
 7     Console.WriteLine(typeItem.FullName);
 8 }
 9 Console.WriteLine("------------枚举出模块------------");
10 foreach (var moduleItem in assembly.GetModules())
11 {
12     Console.WriteLine(moduleItem.Name);
13 }
载入dll枚举出类型和模块

 

1 // 创建实例
2 var obj = assembly.CreateInstance("Demo02_DLL.Test");
3 Type myType = obj.GetType();
创建实例

 

1 // 使用带参数构造函数创建实例
2 assembly.CreateInstance("Demo02_DLL.Test", false, BindingFlags.Default, null, new object[] { "构造函数" }, null, null);
使用带参数构造函数创建实例

 

1 foreach (var memberItem in myType.GetMembers())
2 {
3     Console.WriteLine(memberItem.Name);
4 }
枚举出所有成员

 

1 // 枚举出所有方法
2 foreach (var methodItem in myType.GetMethods())
3 {
4     Console.WriteLine(methodItem.Name);
5     foreach (var parameterItem in methodItem.GetParameters())
6     {
7         Console.WriteLine($"参数:{parameterItem.Name},类型:{parameterItem.ParameterType}");
8     }
9 }
枚举出所有方法

 

1 // 调用非重载方法
2 MethodInfo methodInfo = myType.GetMethod("Show");
3 methodInfo.Invoke(obj, null); //有参数 methodInfo.Invoke(obj, new object[] {123,"我爱你中国" });
调用非重载方法

 

1 // 调用重载方法
2 MethodInfo methodInfo = myType.GetMethod("Sing",new Type[] { typeof(int)});//找到只有1个int参数的Sing方法
3 methodInfo.Invoke(obj, new object[] { 123 });
调用重载方法

 

1 // 枚举出所有字段
2 foreach (var fieldItem in myType.GetFields())
3 {
4     Console.WriteLine(fieldItem.Name);
5 }
枚举出所有字段

 

1 // 读写字段值
2 FieldInfo fieldInfo = myType.GetField("Id"); 
3 fieldInfo.SetValue(obj, 234);  // 赋值
4 Console.WriteLine(fieldInfo.GetValue(obj)); // 取值
读写字段值

 

1 // 枚举出所有属性
2 foreach (var propertyItem in myType.GetProperties())
3 {
4     Console.WriteLine(propertyItem.Name);
5 }
枚举出所有属性

 

1 // 读写属性值
2 PropertyInfo propertyInfo = myType.GetProperty("Name");
3 propertyInfo.SetValue(obj, "离离原上草");  // 赋值
4 Console.WriteLine(propertyInfo.GetValue(obj));  // 取值
读写属性值

 

 1 // 完整代码
 2 static void Main(string[] args)
 3 {
 4     string dllPath = @"D:\学习\CSharp技术栈入门到精通系列\Demo02-DLL\bin\Debug\net5.0\Demo02-DLL.dll";
 5     Assembly assembly = Assembly.LoadFile(dllPath);
 6     Console.WriteLine("------------枚举出类型------------");
 7     foreach (var typeItem in assembly.GetTypes())
 8     {
 9         Console.WriteLine(typeItem.FullName);
10     }
11     Console.WriteLine("------------枚举出模块------------");
12     foreach (var moduleItem in assembly.GetModules())
13     {
14         Console.WriteLine(moduleItem.Name);
15     }
16 
17     var obj = assembly.CreateInstance("Demo02_DLL.Test");
18     Type myType = obj.GetType();
19     Console.WriteLine("------------枚举出Member------------");
20     foreach (var memberItem in myType.GetMembers())
21     {
22         Console.WriteLine(memberItem.Name);
23     }
24     Console.WriteLine("------------枚举出Method------------");
25     foreach (var methodItem in myType.GetMethods())
26     {
27         Console.WriteLine(methodItem.Name);
28         foreach (var parameterItem in methodItem.GetParameters())
29         {
30             Console.WriteLine($"参数:{parameterItem.Name},类型:{parameterItem.ParameterType}");
31         }
32     }
33     Console.WriteLine("------------调用非重载Method------------");
34     MethodInfo methodInfo = myType.GetMethod("Show");
35     methodInfo.Invoke(obj, null); //有参数 methodInfo.Invoke(obj, new object[] {123,"我爱你中国" });
36     //Console.WriteLine("------------调用重载Method------------");
37     //MethodInfo methodInfo = myType.GetMethod("Sing",new Type[] { typeof(int)});//找到只有1个int参数的Sing方法
38     //methodInfo.Invoke(obj, new object[] { 123 });
39     Console.WriteLine("------------枚举出Field------------");
40     foreach (var fieldItem in myType.GetFields())
41     {
42         Console.WriteLine(fieldItem.Name);
43     }
44     Console.WriteLine("------------字段读写值------------");
45     FieldInfo fieldInfo = myType.GetField("Id");
46     fieldInfo.SetValue(obj, 234);
47     Console.WriteLine(fieldInfo.GetValue(obj));
48     Console.WriteLine("------------枚举出Property------------");
49     foreach (var propertyItem in myType.GetProperties())
50     {
51         Console.WriteLine(propertyItem.Name);
52     }
53     Console.WriteLine("------------属性读写值------------");
54     PropertyInfo propertyInfo = myType.GetProperty("Name");
55     propertyInfo.SetValue(obj, "离离原上草");
56     Console.WriteLine(propertyInfo.GetValue(obj));
57     Console.ReadKey();
58 }    
完整代码

 

2.3、泛型反射

   同样的,做泛型反射实操前先准备一个dll,下面贴上源码,我是用NET5编译的类库。

 1 // NET5泛型反射使用类库
 2 using System;
 3 
 4 namespace Demo02_DLL_Generic
 5 {
 6     public class TestGeneric<T> where T:class
 7     {
 8         public int Id = 1;
 9         public T MyProperty { get; set; }
10         public delegate void DelegateTest<in D>(D tPara);
11         public void TestMothed()
12         {
13             DelegateTest<string> delegateTest = str => {
14                 Console.WriteLine($"Type:{str.GetType()},Value:{str}"); //str就是字符串类型
15             };
16             delegateTest.Invoke("离离原上草,一岁一枯荣,野火烧不尽,春风吹又生");
17         }
18 
19         public void TestMothed(T tPara)
20         {
21             Console.WriteLine($"Type:{tPara.GetType()},Value:{tPara}");
22         }
23         public void TestMothed<K>(T tPara,K kPara)
24         {
25             Console.WriteLine($"TypeT:{tPara.GetType()},ValueT:{tPara},TypeK:{kPara.GetType()},ValueK:{kPara}");
26         }
27     }
28 }
NET5泛型反射使用类库

 

 1 // 载入类库dll枚举出类型和模块
 2 string dllPath = @"D:\学习\CSharp技术栈入门到精通系列\Demo02-DLL-Generic\bin\Debug\net5.0\Demo02-DLL-Generic.dll";
 3 Assembly assembly = Assembly.LoadFile(dllPath);
 4 Console.WriteLine("------------枚举出类型------------");
 5 foreach (var typeItem in assembly.GetTypes())
 6 {
 7     Console.WriteLine(typeItem.FullName);
 8 }
 9 Console.WriteLine("------------枚举出模块------------");
10 foreach (var moduleItem in assembly.GetModules())
11 {
12     Console.WriteLine(moduleItem.Name);
13 }
载入类库dll枚举出类型和模块

 

1 // 创建泛型实例
2 Type myType = assembly.GetType("Demo02_DLL_Generic.TestGeneric`1"); //获取泛型
3 myType = myType.MakeGenericType(typeof(string)); //指定泛型类型参数
4 var obj = Activator.CreateInstance(myType); //创建实例
创建泛型实例

 

1 // 枚举出所有成员
2 Console.WriteLine("------------枚举出Member------------");
3 foreach (var memberItem in myType.GetMembers())
4 {
5     Console.WriteLine(memberItem.Name);
6 }
枚举出所有成员

 

 1 // 枚举出所有方法
 2 Console.WriteLine("------------枚举出Method------------");
 3 foreach (var methodItem in myType.GetMethods())
 4 {
 5     Console.WriteLine(methodItem.Name);
 6     foreach (var parameterItem in methodItem.GetParameters())
 7     {
 8         Console.WriteLine($"参数:{parameterItem.Name},类型:{parameterItem.ParameterType}");
 9     }
10 }
枚举出所有方法

 

1 // 调用重载泛型方法
2 Console.WriteLine("------------调用重载泛型方法------------");
3 MethodInfo methodInfo1 = myType.GetMethod("TestMothed",new Type[] { typeof(string) });
4 methodInfo1.Invoke(obj, new object[] { "我爱你中国" });
5 
6 // 筛选出重载泛型方法,效率不高,你有好方法可以告诉我
7 MethodInfo methodInfo2 = myType.GetMethods().First(m => m.Name== "TestMothed" && m.IsGenericMethod && m.GetParameters().Length==2);
8 methodInfo2 = methodInfo2.MakeGenericMethod(typeof(DateTime)); //指定泛型参数
9 methodInfo2.Invoke(obj, new object[] { "飞流直下三千尺,疑是银河落九天",DateTime.Now });
调用重载泛型方法

 

1 // 枚举出所有字段
2 foreach (var fieldItem in myType.GetFields())
3 {
4     Console.WriteLine(fieldItem.Name);
5 }
枚举出所有字段

 

1 // 读写字段值
2 FieldInfo fieldInfo = myType.GetField("Id");
3 fieldInfo.SetValue(obj, 234);
4 Console.WriteLine(fieldInfo.GetValue(obj));
读写字段值

 

1 // 枚举出所有属性
2 foreach (var propertyItem in myType.GetProperties())
3 {
4     Console.WriteLine(propertyItem.Name);
5 }
枚举出所有属性

 

1 // 读写属性值
2 PropertyInfo propertyInfo = myType.GetProperty("MyProperty");
3 propertyInfo.SetValue(obj, "离离原上草");
4 Console.WriteLine(propertyInfo.GetValue(obj));
读写属性值

 

3、扩展

3.1、通过配置修改数据库类型

  有些开发场景需要在多个数据库中切换,我们可以把不同数据库的数据层编译为一个类库,在主程序里切换这个类库的路径来切换数据库。我们分别建立3个类库,Demo02_Mock_Redis、Demo02_Mock_Oracle和Demo02_Mock_MongoDB,下面是代码

 1 // Demo02_Mock_Redis
 2 namespace Demo02_Mock_Redis
 3 {
 4     public class DBContext
 5     {
 6         public void GetAll()
 7         {
 8             Console.WriteLine($"这是Redis数据库");
 9         }
10     }
11 }
Demo02_Mock_Redis

 

 1 // Demo02_Mock_Oracle
 2 namespace Demo02_Mock_Oracle
 3 {
 4     public class DBContext
 5     {
 6         public void GetAll()
 7         {
 8             Console.WriteLine($"这是Oracle数据库");
 9         }
10     }
11 }
Demo02_Mock_Oracle

 

 1 // Demo02_Mock_MongoDB
 2 namespace Demo02_Mock_MongoDB
 3 {
 4     public class DBContext
 5     {
 6         public void GetAll()
 7         {
 8             Console.WriteLine($"这是MongoDB数据库");
 9         }
10     }
11 }
Demo02_Mock_MongoDB

 

添加一个NET5的控制台程序,添加一个配置文件App.config,使用的时候,可以在这里修改数据库类库的地址

1 <?xml version="1.0" encoding="utf-8" ?>
2 <configuration>
3     <appSettings>
4         <add key="dbContext" value="D:\学习\CSharp技术栈入门到精通系列\Demo02-Mock-Redis\bin\Debug\net5.0\Demo02-Mock-Redis.dll"/>
5         <!--    <add key="dbContext" value="D:\学习\CSharp技术栈入门到精通系列\Demo02-Mock-Oracle\bin\Debug\net5.0\Demo02-Mock-Oracle.dll"/>  -->
6         <!--    <add key="dbContext" value="D:\学习\CSharp技术栈入门到精通系列\Demo02-Mock-MongoDB\bin\Debug\net5.0\Demo02-Mock-MongoDB.dll"/>   -->
7     </appSettings>
8 </configuration>
App.config

 

 1 // 主程序
 2 static void Main(string[] args)
 3 {
 4     string dbContextPath = ConfigurationManager.AppSettings["dbContext"];
 5     Assembly assembly = Assembly.LoadFile(dbContextPath);
 6     Type myType = assembly.GetTypes().First(t => t.Name.Contains("DBContext"));
 7     var obj = Activator.CreateInstance(myType); //创建实例
 8     MethodInfo getMethod = myType.GetMethod("GetAll"); //获取GetALL方法
 9     getMethod.Invoke(obj,new object[] { }); //执行方法
10 
11     Console.ReadKey();
12 }
主程序

 

一次编译得到主程序后,就可以通过跳转App.config里的配置来修改数据库了

  

 

源码下载

 

 4、参考

反射 (C#) https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/reflection

使用反射访问特性 (C#)  https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/attributes/accessing-attributes-by-using-reflection

.NET 中的反射  https://docs.microsoft.com/zh-cn/dotnet/framework/reflection-and-codedom/reflection

处理反射消息 https://docs.microsoft.com/zh-cn/cpp/mfc/handling-reflected-messages?view=msvc-160

查看类型信息 https://docs.microsoft.com/zh-cn/dotnet/framework/reflection-and-codedom/viewing-type-information

反射类型和泛型类型 https://docs.microsoft.com/zh-cn/dotnet/framework/reflection-and-codedom/reflection-and-generic-types

如何:使用反射检查和实例化泛型类型   https://docs.microsoft.com/zh-cn/dotnet/framework/reflection-and-codedom/how-to-examine-and-instantiate-generic-types-with-reflection

如何:使用反射发出定义泛型类型   https://docs.microsoft.com/zh-cn/dotnet/framework/reflection-and-codedom/how-to-define-a-generic-type-with-reflection-emit

一种获取重载泛型方法的方式  https://blog.csdn.net/anyingzhi4630/article/details/102017239?utm_medium=distribute.pc_relevant_bbs_down.none-task-blog-baidujs-1.nonecase&depth_1-utm_source=distribute.pc_relevant_bbs_down.none-task-blog-baidujs-1.nonecase

posted on 2021-03-01 11:58  BigBox777  阅读(386)  评论(2编辑  收藏  举报