阅读目录
//==================================实现反射注入================================ 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());
开始阅读 完整代码下载
反射提供描述程序集、模块和类型的对象(Type 类型)。 可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型,然后调用其方法或访问其字段和属性。 如果代码中使用了特性,可以利用反射来访问它们。大白话说就是,可以把编译成exe或者dll的程序集,加载到内存创建对象来使用,后面会扩展一个案例,程序集里面通过配置切换多个数据库使用。
做实现之前需要准备一个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 }
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 }
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 }
同样的,做泛型反射实操前先准备一个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 }
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 }
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个类库,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 }
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 }
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 }
添加一个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>
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里的配置来修改数据库了
反射 (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