C#进阶学习4--反射(Reflection)
一.反射的定义
审查元数据并收集关于它的类型信息的能力。
二.基础概念
(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"
public class Person
{
private int id;
public int Id { get => id; set => id = value; }
public string Name { set; get; }
public string Phone { get; set; }
public Person()
{
}
public Person(string a, int b)
{
this.Name = a;
this.Phone = b.ToString();
}
public Person(int id, string name, string phone)
{
this.Id = id;
this.Name = name;
this.Phone = phone;
}
public string getName()
{
return this.Name;
}
public string getName1(string str)
{
return str;
}
public string getPhone()
{
return this.Phone;
}
public string getPhone(string str)
{
return this.Phone+str;
}
public string getPhone(string str,int num)
{
return this.Phone + str+num;
}
private void privateMethod(string a)
{
Console.WriteLine("这是一个私有方法,传入的参数是:"+a);
}
}
1.创建不带参数的对象
创建不带成熟的对象,本质是就是调用无参的构造函数,具体实现如下
/// <summary>
/// 创建不带参数的对象
/// </summary>
/// <returns></returns>
private static Person CreateWithoutParms()
{
Assembly assembly = Assembly.Load("TestClass");//加载程序集
Type type = assembly.GetType("TestClass.Person");//获取类名称(要带上命名空间)
object o = Activator.CreateInstance(type);//创建Person实体,无参构造
Person person = o as Person;
return person;
}
在控制台中调用
Person person = CreateWithoutParms();
person.Name = "张三";
Console.WriteLine("person.Name:"+ person.Name);
返回结果如下:
成功调用了创建了Person,并调用了Person的无参构造方法
2.创建带参数的对象
创建带成熟的对象,本质是就是调用带参数的构造函数,具体实现如下
/// <summary>
/// 创建带参数的对象
/// </summary>
/// <returns></returns>
private static Person CreateWithParms()
{
Assembly assembly = Assembly.Load("TestClass");//加载程序集
Type type = assembly.GetType("TestClass.Person");//获取类名称(要带上命名空间)
object o = Activator.CreateInstance(type, new object[] {"a",666 });//创建Person实体,有参构造
Person person = o as Person;
return person;
}
在控制台中调用
Person person = CreateWithParms();
Console.WriteLine("person.Name:"+person.Name+ " person.Phone:" + person.Phone);
返回结果如下:
成功调用了创建了Person,并利用带参数的构造直接给属性赋值
说明:如果构造函数为私有的,可以在创建实例时,将CreateInstance中的nonPublic参数设置为true,即可使用私有的构造函数创建实例
object o = Activator.CreateInstance(type,true);
3.调用公共方法
利用反射调用第三方类的方法,可以通过反射得到对应的对象之后,利用得到的对象来执行对象中的方法,但是在这里,主要讲解通过反射,直接调用第三方类中的方法,具体实现如下
/// <summary>
/// 调用带参数的方法(无重载)
/// </summary>
/// <returns></returns>
private static string CallFunction()
{
Assembly assembly= Assembly.Load("TestClass");
Type type = assembly.GetType("TestClass.Person");
object o = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod("getName1");
string result=methodInfo.Invoke(o, new object[] { "这是传入参数" }).ToString();
return result;
}
在控制台中调用
string rsult = CallFunction();
Console.WriteLine(rsult);
返回结果如下:
在这里我们看到,利用反射成功调用了getName1方法,需要注意的是,getName1方法并没有任何重载,如果需要调用带有重载的方法,需要用下面的方法,这里我们假设需要调用getPhone(string str,int num)方法
private static string CallFunctionWithOverload()
{
Assembly assembly = Assembly.Load("TestClass");
Type type = assembly.GetType("TestClass.Person");
object o = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod("getPhone", new Type[] { typeof(string), typeof(int) });//在这里需要把参数类型数组传递给GetMethod方法
string result=methodInfo.Invoke(o, new object[] { "这是传入的String参数", 666 }).ToString();
return result;
}
在控制台中调用
string rsult = CallFunctionWithOverload();
Console.WriteLine(rsult);
返回结果如下:
通过以上的例子,我们可以看到,调用有重载和无重载方法的关键,就是在GetMethod中是否传递参数的类型。
下面写一个综合的例子,调用Person类中的所有方法,并输出结果,如果参数类型为String,则默认传"AAA",如果参数类型为Int,则默认传666,实现方法如下:
private static void CallAllFunction()
{
Assembly assembly = Assembly.Load("TestClass");
Type type = assembly.GetType("TestClass.Person");
object o = Activator.CreateInstance(type);
foreach (MethodInfo methodInfoItem in type.GetMethods())
{
Console.WriteLine("执行"+ methodInfoItem.Name+ "方法");
List<object> objectList = new List<object>();
foreach (ParameterInfo parameterInfoItem in methodInfoItem.GetParameters())
{
if (parameterInfoItem.ParameterType == typeof(String))
{
objectList.Add("AAA");
}
else if (parameterInfoItem.ParameterType == typeof(int))
{
objectList.Add(666);
}
}
try//这里使用try...catch...是为了简化处理属性获取失败导致程序报错问题
{
string result = methodInfoItem.Invoke(o, objectList.ToArray()).ToString();
Console.WriteLine("结果为:" + result);
}
catch
{
}
}
}
调用后返回结果如下:

在这里我们看到,Person中的方法已经全部执行,包括所有的系统方法
4.调用私有方法
/// <summary>
/// 调用私有方法
/// </summary>
private static void CallPrivateFunction()
{
Assembly assembly = Assembly.Load("TestClass");
Type type = assembly.GetType("TestClass.Person");
object o = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod("privateMethod", BindingFlags.Instance | BindingFlags.NonPublic);
methodInfo.Invoke(o, new object[] { "张三"});
}
调用后返回结果如下:
通过以上例子,我们不难发现,调用公共方法与私有方法的区别就是在调用type的GetMethod方法时,是否设置"BindingFlags.Instance | BindingFlags.NonPublic"
5.获取与操作属性
/// <summary>
/// 获取与操作属性
/// </summary>
/// <param name="propertyName"></param>
/// <param name="propertyValue"></param>
private static void getAndSetProperity(string propertyName,string propertyValue)
{
//1.通过反射创建Person实体
Assembly assembly = Assembly.Load("TestClass");
Type type = assembly.GetType("TestClass.Person");
object o = Activator.CreateInstance(type, new object[] { "张三", 131000000 });
PropertyInfo propertyInfo=type.GetProperty(propertyName);
Console.WriteLine("修改前Phone:"+ propertyInfo.GetValue(o));//获取属性值
propertyInfo.SetValue(o, propertyValue);//设置属性值
Console.WriteLine("修改后Phone:" + propertyInfo.GetValue(o));
}
调用后返回结果如下:
通过以上例子,可以发现,获取与设置属性的关键方法分别为GetValue与SetValue,关键传入参数为通过反射得到的实体类
6.获取与操作字段
/// <summary>
/// 获取与操作字段
/// </summary>
/// <param name="fieldName"></param>
/// <param name="fieldValue"></param>
private static void getAndSetField(string fieldName, int fieldValue)
{
Assembly assembly = Assembly.Load("TestClass");
Type type = assembly.GetType("TestClass.Person");
object o = Activator.CreateInstance(type, new object[] {1, "张三", "131000000" });
FieldInfo fieldInfo = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
Console.WriteLine("修改前id"+ fieldInfo.GetValue(o));
fieldInfo.SetValue(o, fieldValue);
Console.WriteLine("修改后id" + fieldInfo.GetValue(o));
}
调用后返回结果如下:
设置和操作字段的方法与设置和操作属性的方法基本一直,需要注意的是,在用type的GetField方法时,如果获取或设置的是私有字段,需要设置该方法的可访问属性,本例中的设置为"BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance"
接下来,我们继续研究反射在泛型中的作用,在进一步研究之前,我们先定义如下泛型类,同以上实体类一样,假设该泛型类位于一个第三方的类库下,类库名称为“TestClass”,类名为"GenericClass"
public class GenericClass<X,Y,Z>
{
public X xxx{ set; get; }
public Y yyy { set; get; }
public Z zzz { set; get; }
public void PrintParm<A,B,C>(A a, B b, C c)
{
Console.WriteLine("A的类型为" + a.GetType().Name + ",值为" + a.ToString());
Console.WriteLine("B的类型为" + b.GetType().Name + ",值为" + b.ToString());
Console.WriteLine("C的类型为" + c.GetType().Name + ",值为" + c.ToString());
}
}
7.创建泛型类并调用
/// <summary>
/// 调用泛型类中的方法
/// </summary>
private static void GenericWithParms()
{
Assembly assembly = Assembly.Load("TestClass");
Type type = assembly.GetType("TestClass.GenericClass`3");
type= type.MakeGenericType(new Type[] { typeof(string),typeof(int),typeof(DateTime)});
object o = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod("PrintParm");
methodInfo = methodInfo.MakeGenericMethod(new Type[] { typeof(string), typeof(int), typeof(DateTime) });
methodInfo.Invoke(o, new object[] {"张三",666,DateTime.Now});
}
调用后返回结果如下:
针对以上代码,做出以下几点说明:
1).
只有在创建泛型类时,才需要做这样的设置,数字为泛型类总参数的个数
2).
在创建泛型实体之前,要通过type的MakeGenericType方法,设置传入的参数类型
3).
同创建泛型类一样,在调用泛型方法前,也需要设置泛型方法的参数类型
4).如果调用的是泛型类中的普通方法,无需设置泛型方法的参数类型,反之,如果调用的是普通类中的泛型方法,无需设置泛型类参数个数,也无需设置参数类型
至此,反射的常用方式讲解完毕...
C#进阶学习4--反射(Reflection)的更多相关文章
- 4 Java学习之 反射Reflection
1. 反射概念 反射机制就是:动态地获取类的一切信息,并利用这些信息做一些你想做的事情. java反射机制能够知道类名而不实例化对象的状态下,获得对象的属性或调用方法. JAVA反射机制是在运行状态 ...
- [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程
[.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程 本节导读:本节主要介绍什么是.NET反射特性,.NET反射能为我们做些什么,最后介绍几种常用的 ...
- [.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦
[.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦 本节导读:上篇文章简单介绍了.NET面向对象中一个重要的技术反射的基本应用,它可以让我们动态的调 ...
- Laravel学习笔记之PHP反射(Reflection) (上)
Laravel学习笔记之PHP反射(Reflection) (上) laravel php reflect 2.1k 次阅读 · 读完需要 80 分钟 3 说明:Laravel中经常使用PHP的反 ...
- 【C++】近期C++特性进阶学习总结(一)
前言 C++的特性多的数不胜数,语言标准也很多,所以不定期对近期所学的C++知识进行总结,是对自身知识体系检查的良好机会,顺便锻炼一下写博客的文笔 三/五/零之法则 三之法则:如果某个类需要用户定义的 ...
- [读行者][学习LinqExpression和Reflection(Emit)]阅读TypeBuilderSample之ExampleFromTheArticle
前言 关于”读行者“ 俗语有云:"读万卷书,行万里路“.多读一些优秀代码,不仅可以锻炼我们读代码的能力(便于维护或相互交流),还可以吸取很多我们成长所需的知识点.多读,才能开阔我们的眼界,才 ...
- java学习之反射机制
java语言区别于C,C++等准静态语言的最大特点就是java的反射机制.静态语言的最直接定义就是不能在运行时改变程序结构或变量的类型.按照这样的定义,python,ruby是动态语言,C,C++,J ...
- Java7编程高级进阶学习笔记
本书PDF 下载地址: http://pan.baidu.com/s/1c141KGS 密码:v6i1 注:本文有空会跟新: 讲述的是jdk7的内容: 注关于java 更详细的内容请进入:<Ja ...
- C# 反射(Reflection)技术
本文参考自C#反射(Reflection)详解,纯属学习笔记,加深记忆 在介绍反射前,先介绍一个重要的知识点 .Net应用程序是由程序集(Assembly).模块(Module).类型 ...
随机推荐
- Nifi:nifi内置处理器Processor的开发
本篇主要是介绍自定义处理器的开发方式及Nifi处理器开发的一些细节 Nifi-Processor自定义开发的流程 之前说过,大部分的数据处理,我们可以基于ExcuseGroovyScript处理器,编 ...
- [DB] Spark Core (3)
高级算子 mapPartitionWithIndex:对RDD中每个分区(有下标)进行操作,通过自己定义的一个函数来处理 def mapPartitionsWithIndex[U](f: (Int, ...
- 二进制部署K8S-1基本概念
二进制部署K8S-1基本概念 感谢老男孩教育王导的公开视频,文档整理自https://www.yuque.com/duduniao/k8s. 1.实验环境 1.1 虚拟机 因为在后期运行容器需要有大量 ...
- rsync 服务配置_rsync命令使用方法
rsync介绍 rsync用来定时备份服务器中的文件或者目录,有三种工作模式,本地复制,使用系统用户认证,守护进程方式,开源高效.同步工具,把一台机器上的文件同步都另一台机器 .默认使用873端口 选 ...
- IT菜鸟之总结(Du teacher)
初次接触云计算,从以前对计算机的一窍不通,经过这三周的学习,起码是通了一窍了:哈哈,至少是对计算机的组成及系统的安装都有了认识,也初次学习了Linux系统,对其的发展和使用有了认识,也学到了一些基础的 ...
- Linux进阶之链路聚合
CentOS7用命令配置链路聚合 链路聚合是一个计算机网络术语,将多个物理端口汇聚在一起,形成一个逻辑端口,以实现出入流量在各成员端口的负荷分担,交换机根据用户配置的端口负荷分担策略决定网络封包从哪个 ...
- linux初级之总结复习
一.linux命令复习 1.ls:列出当前目录下的文件 -h: -l: -d: -a: 2. man: 命令帮助手册 3. cd: 切换目录 -: ~: ..: cd: 4. pwd: 显示当前工作 ...
- centos下yum方法安装apache+php+mysql
yum(全称为:Yellow dog Updater,Modified) 是一个在Fedora和RedHat以及SUSE中的Shell前端管理软件.基于RPM包管理,能够从远处镜像服务器下载RPM包并 ...
- 《MySQL面试小抄》索引考点一面总结
<MySQL面试小抄>索引考点一面总结 我是肥哥,一名不专业的面试官! 我是囧囧,一名积极找工作的小菜鸟 囧囧表示:面试最怕的就是面试官问的知识点太笼统,自己无法快速定位到关键问题点!!! ...
- TensorFlow解析常量、变量和占位符
TensorFlow解析常量.变量和占位符 最基本的 TensorFlow 提供了一个库来定义和执行对张量的各种数学运算.张量,可理解为一个 n 维矩阵,所有类型的数据,包括标量.矢量和矩阵等都是特殊 ...