zoukankan      html  css  js  c++  java
  • C#技术栈入门到精通系列2——反射Reflection

    阅读目录

    1、介绍

    2、实现

      2.1、非泛型反射

      2.3、泛型反射

    3、扩展

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

     4、参考

    返回系列文章目录 

    开始阅读  完整代码下载

    1、介绍

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

    2、实现

    2.1、非泛型反射

      做实现之前需要准备一个dll文件,我这里用的net5编译的,下面给出源代码,编译好后,另外创建一个项目来实现非泛型反射的实现。

     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-DLLinDebug
    et5.0Demo02-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-DLLinDebug
    et5.0Demo02-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-GenericinDebug
    et5.0Demo02-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-RedisinDebug
    et5.0Demo02-Mock-Redis.dll"/>
    5         <!--    <add key="dbContext" value="D:学习CSharp技术栈入门到精通系列Demo02-Mock-OracleinDebug
    et5.0Demo02-Mock-Oracle.dll"/>  -->
    6         <!--    <add key="dbContext" value="D:学习CSharp技术栈入门到精通系列Demo02-Mock-MongoDBinDebug
    et5.0Demo02-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

  • 相关阅读:
    XNA之3D文字
    SQL2005调用C#编写的DLL
    C#绘图工具之Rotate
    ASP.NET中的WebService
    数据库同步之复制技术
    C#之TCP消息的发送和接受
    Tsql清空表数据的两种方式truncate and delete
    Code First Migrations数据迁移方法
    MSSQLSERVER跨服务器连接
    windows下wget命令行下载工具的使用
  • 原文地址:https://www.cnblogs.com/bigbox777/p/14414489.html
Copyright © 2011-2022 走看看