• 售前

  • 售后

热门帖子
入门百科

C#进阶学习--反射(Reflection)

[复制链接]
南辕北辙395 显示全部楼层 发表于 2022-1-15 23:46:42 |阅读模式 打印 上一主题 下一主题
一.反射的界说

查察元数据并网络关于它的范例信息的本事。
二.根本概念

(1)Assembly:界说和加载步调集,加载在步调会集的全部模块以及今后步调会集查找范例并创建该范例的实例。
(2)Module:获取包罗模块的步调集以及模块中的类等,还可以获取在模块上界说的全部全局方法或其他特定的非全局方法。
(3)ConstructorInfo:获取构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。
(4)MethodInfo(GetMethod/GetMethods):获取方法的名称、返回范例、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。
(5)FiedInfo(GetField/GetFields):获取字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。
(6)EventInfo(GetEvent/GetEvents):获取变乱的名称、变乱处理惩罚步调数据范例、自界说属性、声明范例和反射范例等,添加或移除变乱处理惩罚步调。
(7)PropertyInfo(GetProperty/GetProperties):获取属性的名称、数据范例、声明范例、反射范例和只读或可写状态等,获取或设置属性值。
(8)ParameterInfo:获取参数的名称、数据范例、是输入参数照旧输出参数,以及参数在方法署名中的位置等。
(9)MemberInfo(GetMember/GetMembers):获取字段、变乱、属性等各种信息
三.反射作用

在演示反射的作用之前,我们先界说如下实体类,假设该实体类位于一个第三方的类库下,类库名称为“TestClass”,类名为"Person"
  1.     public class Person
  2.     {
  3.         private int id;
  4.         public int Id { get => id; set => id = value; }
  5.         public string Name { set; get; }
  6.         public string Phone { get; set; }      
  7.         public Person()
  8.         {
  9.            
  10.         }
  11.         public Person(string a, int b)
  12.         {
  13.             this.Name = a;
  14.             this.Phone = b.ToString();
  15.         }
  16.         public Person(int id, string name, string phone)
  17.         {
  18.             this.Id = id;
  19.             this.Name = name;
  20.             this.Phone = phone;
  21.         }        
  22.         public string getName()
  23.         {
  24.             return this.Name;            
  25.         }
  26.         public string getName1(string str)
  27.         {
  28.             return str;
  29.         }      
  30.         public string getPhone()
  31.         {
  32.             return this.Phone;
  33.         }
  34.         public string getPhone(string str)
  35.         {
  36.             return this.Phone+str;
  37.         }
  38.         public string getPhone(string str,int num)
  39.         {
  40.             return this.Phone + str+num;
  41.         }
  42.         private void privateMethod(string a)
  43.         {
  44.             Console.WriteLine("这是一个私有方法,传入的参数是:"+a);
  45.         }  
  46.     }
复制代码
1.创建不带参数的对象

创建不带成熟的对象,本质是就是调用无参的构造函数,详细实现如下
  1.         /// <summary>
  2.         /// 创建不带参数的对象
  3.         /// </summary>
  4.         /// <returns></returns>
  5.         private static Person CreateWithoutParms()
  6.         {
  7.             Assembly assembly = Assembly.Load("TestClass");//加载程序集
  8.             Type type = assembly.GetType("TestClass.Person");//获取类名称(要带上命名空间)
  9.             object o = Activator.CreateInstance(type);//创建Person实体,无参构造
  10.             Person person = o as Person;
  11.             return person;
  12.         }
复制代码
在控制台中调用
  1.             Person person = CreateWithoutParms();
  2.             person.Name = "张三";
  3.             Console.WriteLine("person.Name:"+ person.Name);
复制代码
返回效果如下:
乐成调用了创建了Person,并调用了Person的无参构造方法
2.创建带参数的对象

创建带成熟的对象,本质是就是调用带参数的构造函数,详细实现如下
  1.         /// <summary>
  2.         /// 创建带参数的对象
  3.         /// </summary>
  4.         /// <returns></returns>
  5.         private static Person CreateWithParms()
  6.         {
  7.             Assembly assembly = Assembly.Load("TestClass");//加载程序集
  8.             Type type = assembly.GetType("TestClass.Person");//获取类名称(要带上命名空间)
  9.             object o = Activator.CreateInstance(type, new object[] {"a",666 });//创建Person实体,有参构造
  10.             Person person = o as Person;
  11.             return person;
  12.         }
复制代码
在控制台中调用
  1.             Person person = CreateWithParms();
  2.             Console.WriteLine("person.Name:"+person.Name+ " person.Phone:" + person.Phone);
复制代码
返回效果如下:
乐成调用了创建了Person,并利用带参数的构造直接给属性赋值
分析:如果构造函数为私有的,可以在创建实例时,将CreateInstance中的nonPublic参数设置为true,即可利用私有的构造函数创建实例

  1.             object o = Activator.CreateInstance(type,true);
复制代码
3.调用公共方法

利用反射调用第三方类的方法,可以通过反射得到对应的对象之后,利用得到的对象来实行对象中的方法,但是在这里,紧张解说通过反射,直接调用第三方类中的方法,详细实现如下
  1.         /// <summary>
  2.         /// 调用带参数的方法(无重载)
  3.         /// </summary>
  4.         /// <returns></returns>
  5.         private static string CallFunction()
  6.         {
  7.             Assembly assembly= Assembly.Load("TestClass");
  8.             Type type = assembly.GetType("TestClass.Person");
  9.             object o = Activator.CreateInstance(type);
  10.             MethodInfo methodInfo = type.GetMethod("getName1");
  11.             string result=methodInfo.Invoke(o, new object[] { "这是传入参数" }).ToString();
  12.             return result;
  13.         }
复制代码
在控制台中调用
  1.             string rsult = CallFunction();
  2.             Console.WriteLine(rsult);
复制代码
返回效果如下:
在这里我们看到,利用反射乐成调用了getName1方法,必要留意的是,getName1方法并没有任何重载,如果必要调用带有重载的方法,必要用下面的方法,这里我们假设必要调用getPhone(string str,int num)方法
  1.         private static string CallFunctionWithOverload()
  2.         {
  3.             Assembly assembly = Assembly.Load("TestClass");
  4.             Type type = assembly.GetType("TestClass.Person");
  5.             object o = Activator.CreateInstance(type);
  6.             MethodInfo methodInfo = type.GetMethod("getPhone", new Type[] { typeof(string), typeof(int) });//在这里需要把参数类型数组传递给GetMethod方法
  7.             string result=methodInfo.Invoke(o, new object[] { "这是传入的String参数", 666 }).ToString();
  8.             return result;
  9.         }
复制代码
在控制台中调用
  1.             string rsult = CallFunctionWithOverload();
  2.             Console.WriteLine(rsult);
复制代码
返回效果如下:
通过以上的例子,我们可以看到,调用有重载和无重载方法的关键,就是在GetMethod中是否转达参数的范例。

下面写一个综合的例子,调用Person类中的全部方法,并输出效果,如果参数范例为String,则默认传"AAA",如果参数范例为Int,则默认传666,实现方法如下:
  1.         private static void CallAllFunction()
  2.         {
  3.             Assembly assembly = Assembly.Load("TestClass");
  4.             Type type = assembly.GetType("TestClass.Person");
  5.             object o = Activator.CreateInstance(type);
  6.             foreach (MethodInfo methodInfoItem in type.GetMethods())
  7.             {
  8.                 Console.WriteLine("执行"+ methodInfoItem.Name+ "方法");
  9.                 List<object> objectList = new List<object>();
  10.                 foreach (ParameterInfo parameterInfoItem in methodInfoItem.GetParameters())
  11.                 {
  12.                     if (parameterInfoItem.ParameterType == typeof(String))
  13.                     {
  14.                         objectList.Add("AAA");
  15.                     }
  16.                     else if (parameterInfoItem.ParameterType == typeof(int))
  17.                     {
  18.                         objectList.Add(666);
  19.                     }
  20.                 }
  21.                 try//这里使用try...catch...是为了简化处理属性获取失败导致程序报错问题
  22.                 {
  23.                     string result = methodInfoItem.Invoke(o, objectList.ToArray()).ToString();
  24.                     Console.WriteLine("结果为:" + result);
  25.                 }
  26.                 catch
  27.                 {
  28.                 }
  29.             }
  30.         }
复制代码
调用后返回效果如下:

在这里我们看到,Person中的方法已经全部实行,包罗全部的体系方法
4.调用私有方法

  1.         /// <summary>
  2.         /// 调用私有方法
  3.         /// </summary>
  4.         private static void CallPrivateFunction()
  5.         {
  6.             Assembly assembly = Assembly.Load("TestClass");
  7.             Type type = assembly.GetType("TestClass.Person");
  8.             object o = Activator.CreateInstance(type);
  9.             MethodInfo methodInfo = type.GetMethod("privateMethod", BindingFlags.Instance | BindingFlags.NonPublic);
  10.             methodInfo.Invoke(o, new object[] { "张三"});
  11.         }
复制代码
调用后返回效果如下:
通过以上例子,我们不难发现,调用公共方法与私有方法的区别就是在调用type的GetMethod方法时,是否设置"BindingFlags.Instance | BindingFlags.NonPublic"

5.获取与操纵属性

  1.         /// <summary>
  2.         /// 获取与操作属性
  3.         /// </summary>
  4.         /// <param name="propertyName"></param>
  5.         /// <param name="propertyValue"></param>
  6.         private static void getAndSetProperity(string propertyName,string propertyValue)
  7.         {
  8.             //1.通过反射创建Person实体
  9.             Assembly assembly = Assembly.Load("TestClass");
  10.             Type type = assembly.GetType("TestClass.Person");
  11.             object o = Activator.CreateInstance(type, new object[] { "张三", 131000000 });
  12.             PropertyInfo propertyInfo=type.GetProperty(propertyName);
  13.             Console.WriteLine("修改前Phone:"+ propertyInfo.GetValue(o));//获取属性值
  14.             propertyInfo.SetValue(o, propertyValue);//设置属性值
  15.             Console.WriteLine("修改后Phone:" + propertyInfo.GetValue(o));           
  16.         }
复制代码
调用后返回效果如下:
通过以上例子,可以发现,获取与设置属性的关键方法分别为GetValue与SetValue,关键传入参数为通过反射得到的实体类

6.获取与操纵字段

  1.         /// <summary>
  2.         /// 获取与操作字段
  3.         /// </summary>
  4.         /// <param name="fieldName"></param>
  5.         /// <param name="fieldValue"></param>
  6.         private static void getAndSetField(string fieldName, int fieldValue)
  7.         {
  8.             Assembly assembly = Assembly.Load("TestClass");
  9.             Type type = assembly.GetType("TestClass.Person");
  10.             object o = Activator.CreateInstance(type, new object[] {1, "张三", "131000000" });            
  11.             FieldInfo fieldInfo = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
  12.             Console.WriteLine("修改前id"+ fieldInfo.GetValue(o));
  13.             fieldInfo.SetValue(o, fieldValue);
  14.             Console.WriteLine("修改后id" + fieldInfo.GetValue(o));
  15.         }
复制代码
调用后返回效果如下:
设置和操纵字段的方法与设置和操纵属性的方法根本不绝,必要留意的是,在用type的GetField方法时,如果获取或设置的是私有字段,必要设置该方法的可访问属性,本例中的设置为"BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance"

接下来,我们继承研究反射在泛型中的作用,在进一步研究之前,我们先界说如下泛型类,同以上实体类一样,假设该泛型类位于一个第三方的类库下,类库名称为“TestClass”,类名为"GenericClass"
  1.     public class GenericClass<X,Y,Z>
  2.     {
  3.         public X xxx{ set; get; }
  4.         public Y yyy { set; get; }
  5.         public Z zzz { set; get; }
  6.         public void PrintParm<A,B,C>(A a, B b, C c)
  7.         {
  8.             Console.WriteLine("A的类型为" + a.GetType().Name + ",值为" + a.ToString());
  9.             Console.WriteLine("B的类型为" + b.GetType().Name + ",值为" + b.ToString());
  10.             Console.WriteLine("C的类型为" + c.GetType().Name + ",值为" + c.ToString());
  11.         }
  12.     }
复制代码
7.创建泛型类并调用

  1.         /// <summary>
  2.         /// 调用泛型类中的方法
  3.         /// </summary>
  4.         private static void GenericWithParms()
  5.         {
  6.             Assembly assembly = Assembly.Load("TestClass");
  7.             Type type = assembly.GetType("TestClass.GenericClass`3");
  8.             type= type.MakeGenericType(new Type[] { typeof(string),typeof(int),typeof(DateTime)});
  9.             object o = Activator.CreateInstance(type);
  10.             MethodInfo methodInfo = type.GetMethod("PrintParm");
  11.             methodInfo = methodInfo.MakeGenericMethod(new Type[] { typeof(string), typeof(int), typeof(DateTime) });
  12.             methodInfo.Invoke(o, new object[] {"张三",666,DateTime.Now});
  13.         }
复制代码
调用后返回效果如下:
针对以上代码,做出以下几点分析:
1).
只有在创建泛型类时,才必要做如许的设置,数字为泛型类总参数的个数
2).
在创建泛型实体之前,要通过type的MakeGenericType方法,设置传入的参数范例
3).
同创建泛型类一样,在调用泛型方法前,也必要设置泛型方法的参数范例
4).如果调用的是泛型类中的平常方法,无需设置泛型方法的参数范例,反之,如果调用的是平常类中的泛型方法,无需设置泛型类参数个数,也无需设置参数范例
至此,反射的常用方式解说完毕…
末了,着实全部的数据测试可以在云服务器举行,各人可以看看腾讯云的干系服务,买来作为测试数据的服务器非常不错,最低只要38一年


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作