什么是反射,反射能干嘛?

反射是:指程序可以访问、检测和修改它本身状态或行为的一种能力

反射是一种能力,所以给的定义就是说明了它能干嘛。

我们平时用反射主要做:

  • 获取类型的相关信息
  • 动态调用方法
  • 动态构造对象
  • 从程序集中获得类型。

获取类型的相关信息

反射的核心Type类,Type对象提供的属性和方法可以获取对象的一切信息,如:方法、字段、属性、事件...等等。

我们获取已加载程序集中类型的Type对象的几种方法:(以StringBuilder 类型为例)

  1. 直接使用typeof操作符 Type T1 = typeof(StringBuilder);
  2. 通过类型实例 Type T2 = new StringBuilder().GetType();
  3. 通过Type类的静态方法 Type T3 = Type.GetType("System.IO.Stream");

不管使用那种,我们最终得到的结果都是一样的。

那么我们通过Type又能得到些什么信息呢?

获取类型本身信息(命名空间名、全名、是否是抽象、是否是类、、、等等)

var T1 = typeof(StringBuilder);
Console.WriteLine("命名空间名称:" + T1.Namespace);
Console.WriteLine("直接基类型:" + T1.BaseType);
Console.WriteLine("全名:" + T1.FullName);
Console.WriteLine("是抽象类型:" + T1.IsAbstract);
Console.WriteLine("是类:" + T1.IsClass);
//.....等等

获取类型成员信息(通过Tyep中的方法GetMembers)

Type T1 = typeof(TClass);
var Mets = T1.GetMembers();//获取Type对象的所有公有成员
foreach (var m in Mets)
{Console.WriteLine("【" + m.MemberType.ToString()+ "】:" + m.Name);// m.MemberType 是成员类型
}

MemberType所能包含的成员类型有哪些呢?如:(可以自己可以F12进去看看)

注意:其中MemberInfo的属性DeclaringType返回的是这个属性定义的类型,而ReflectedType返回的是获取这个属性的对象类型。

如:

Type T2 = typeof(TClass);
var Mets = T2.GetMembers();//获取所有公共成员(返回值是MemberInfo类型集合)
foreach (var m in Mets)
{if (m.Name=="Equals"){Console.WriteLine("【" + m.MemberType.ToString() + "】:" + m.Name);// m.MemberType 是成员类型

        // m.DeclaringType;//获取申明该成员的类        // m.ReflectedType;//获取用于获取 MemberInfo 的此实例的类对象。
    }
}

T2中的Equals,我们知道这个方式是在Objec中定义的,在TClass中调用的,所以:

我们发现获取Type对象的成员大多都是以 isxxx、Getxxx、Getxxxs格式的。

isxxx格式的基本上都是判断是否是某类型。

Getxxx和Getxxxs都是放回某类型和某类型集合。其中主要的类型有:

//FieldInfo封装了关于字段的所有信息   (通过Tyep对象的GetFields或GetField方法)//PropertyInfo类型,封装了类型的属性信息;(通过Type对象的GetProperties或GetProperty方法)//ConstructorInfo类型,封装了类型的构造函数信息; (..........)//MethodInfo类型,封装了类型的方法信息;  (........)

//MemberInfo类型,封装了类型的所有公共成员;(**就是我们上面说的GetMembers方法**)//EventInfo类型,封装了类型的事件信息;(.......)//ParameterInfo类型,封装了方法和构造函数的参数信息;(........)

它们都在 System.Reflection 命名空间下,其每个isxxx、Getxxx、Getxxxs的细节实例用法就不一一演示了。和上面的GetMembers用法区别不大。

动态调用方法

首先定义个类:

public class TClass
{public void fun(string str){Console.WriteLine("我是fun方法,我被调用了。" + str);}public void fun2(){Console.WriteLine("我是fun2方法,我被调用了。");}public static void fun3(){Console.WriteLine("我是fun3静态方法,我被调用了");}
}

调用方式一(使用InvokeMember调用方法)

调用带参实例方法fun

Type T1 = typeof(TClass);
T1.InvokeMember("fun", BindingFlags.InvokeMethod, null, new TClass(), new string[] { "test" });

调用无参实例方法fun2

Type T1 = typeof(TClass);
T1.InvokeMember("fun2", BindingFlags.InvokeMethod, null, new TClass(), null);

调用静态方法

Type T1 = typeof(TClass);
T1.InvokeMember("fun3", BindingFlags.InvokeMethod, null, T1, null);

我们发现了一个问题当我们调用实例方法的时候需要传实例对象过去。(有人会说,都实例对象了,我还要你动态掉调用个屁啊。有种情况,在我们实例了对象后,仍不确定应该调用那个方法时可以只有使用。然后有人有说了,那如果实例对象我也不确定呢?那我们下面会分析连实例对象也给动态了。那接着完下看吧。)

我们来说下这几个参数的意思吧。

第一个:要被动态调用的方法名。

第二个:是一个枚举,表示是调用一个方法

第三个:是Binder,传的是null,使用默认值。

第四个:传如实例对象(调用实例方法时)或者Type对象(调用静态方法时)。

第五个:要传给被调用发的参数数组。

调用方式二(使用MethodInfo.Invoke调用方法)

Type T1 = typeof(TClass);
T1.GetMethod("fun", BindingFlags.Instance | BindingFlags.Public).Invoke(new TClass(), new string[] { "testfun1" });
T1.GetMethod("fun2", BindingFlags.Instance | BindingFlags.Public).Invoke(new TClass(), null);
T1.GetMethod("fun3", BindingFlags.Static | BindingFlags.Public).Invoke(T1, null);

使用其实和上面的方式一区别不大。

真正的全动态调用

上面的两种方式,在编写代码的时候总是要先确定了已知的对象名和方法名。那么我们在不知道对象和方法名的时候是否也可以调用呢?答案是肯定的,实现如下:

Console.WriteLine("请输入对象类名");
string className = Console.ReadLine();
Console.WriteLine("请输入要执行的方法名");string funName = Console.ReadLine();
Type T1 = Type.GetType(className);ConstructorInfo ci = T1.GetConstructors()[0]; //获取构造函数
var obj = ci.Invoke(null);//实例化构造函数

T1.InvokeMember(funName, BindingFlags.InvokeMethod, null, obj, null);

当然,这个代码只能只是fun2,因为上面的传参写死了。(你也可以自己稍微修改下,就可以执行fun、fun2、fun3了)

效果如下:(对象名和方法名都是手动输入的)

动态构造对象

我们先定义一个对象:

public class TClass
{public TClass(){Console.WriteLine("构造函数被执行了。。");}public TClass(string str){Console.WriteLine("有参构造函数被执行了。。" + str);}
}

动态构造对象

//动态构造对象,方式一
Assembly asm = Assembly.GetExecutingAssembly();
TClass obj = (TClass)asm.CreateInstance("net.tclass", true);//true:不区分大小写//动态构造对象,方式二
ObjectHandle handler = Activator.CreateInstance(null, " net.TClass");//null:当前程序集
obj = (TClass)handler.Unwrap();//动态构造对象,方式三(构造有参构造函数)
Assembly asm2 = Assembly.GetExecutingAssembly();
obj = (TClass)asm2.CreateInstance("net.tclass", true, BindingFlags.Default, null, new string[] { "test" }, null, null);//true:不区分大小写            

执行效果图:

获取和修改属性

var obj = new TClass();
obj.name = "张三";
Type type = typeof(TClass);
//获取属性
var Name = type.InvokeMember("name", BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance, null,obj, new object[] { }) as string;
Console.WriteLine(obj.name);
//设置属性
type.InvokeMember("name", BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance, null,obj, new object[] { "新属性(李四)" });
Console.WriteLine(obj.name);

//=====================
PropertyInfo[] pros = type.GetProperties(---);//
PropertyInfo pro = null;
var value = pro.GetValue(type);//获取值

从程序集中获得类型

取得当前代码所在程序集(使用GetExecutingAssembly)

Assembly ass = Assembly.GetExecutingAssembly();
Console.WriteLine("当前所在程序集名:"+ass.ManifestModule.Name);
Console.WriteLine("当前所在程序集路径:"+ass.Location);

通过反射加载程序集并创建程序中的类型对象

从程序集中获得类型,这个应该是我们平时用得比较多。如我们所谓的依赖注入和控制反转(这个主题将在下篇博文进行分析)就用到了通过反射从程序集中获取类型。

首先我们还是看看怎么从程序集中获得类型吧。我们可以使用Assembly类型提供的静态方法LoadFrom()或Load(),如:

Assembly asm = Assembly.LoadFrom("Demo.dll");
Assembly asm = Assembly.Load("Demo");

区别:

Assembly asm = Assembly.LoadFrom("net.exe");//需要加后缀,可以指定路径,如下面的
Assembly asm1 = Assembly.LoadFrom(@"C:\01文件\05Svn\BlogsCode\Blogs\Blogs.Web\bin\Blogs.BLL.dll");Assembly asm2 = Assembly.Load("Blogs.BLL");//无需加后缀,不可以指定路径
//Assembly asm3 = Assembly.Load(@"C:\01文件\05Svn\BlogsCode\Blogs\Blogs.Web\bin\Blogs.BLL");//这里会报错
//使用Load可以加载当前程序bin目录行下的程序集或者系统程序集//这里TClass可以是一个接口,那么可以在外面的dll任意实现了。
TClass obj = (TClass)asm2.CreateInstance("net.tclass", true);//true:不区分大小写
obj.fun();//***调用动态加载的dll中的方法***

这样带来的功能是非常强大的。如 我们在没有引用程序集的情况下,也可以使用到程序外的程序集。我们还可以根据不同情况引用不同的程序集。我们甚至还可以通过配置文件来直接配置代码运行时应该加载哪个dll,运行哪个dll中的哪个实现方法。(下篇在讲依赖注入的时候会讲到,同学们继续关注哦~)

从上所知,反射不是某一个概念,而是一类操作的统称。或者说是某些能力的统称。 感觉不好回答反射到底是什么,只能说反射能干什么。它能动态创建对象、动态调用对象方法、动态读取和设置属性和字段、它能动态加载程序外的dll。总的感觉就是大多数都是跟“动态”扯上了关系。


补充:跨程序集反射

如果我们反射A.dll,而A.dll中引用了B.dll,那么在assembly.GetTypes(); //运行到这个地方会弹出如下错误描述

“未处理 System.Reflection.ReflectionTypeLoadException Message="无法加载一个或多个请求的类型。有关更多信息,请检索LoaderExceptions属性。”

这种情况可以

Assembly assembly =  Assembly.LoadFrom("A.dll") ;
Type type = assembly.GetType("xxx.myclassname") ; //传入对应的需要反射的类型 而不能GetTypes。且,应用程序需要应用A.dll锁依赖的B.dll。

本文以同步至《C#基础知识巩固系列》

二、什么是反射、反射可以做些什么相关推荐

  1. 计算机专业可以考航空航天专业研究生吗,我是信息与计算科学专业的大二学生,想考北京航空航天计算机专业方向的研究生,那么从现在起我该做些什么...

    我是信息与计算科学专业的大二学生,想考北京航空航天计算机专业方向的研究生,那么从现在起我该做些什么 答案:4  信息版本:手机版 解决时间 2019-10-05 23:10 已解决 2019-10-0 ...

  2. 二十多岁该做些什么,将来才不会后悔?

    二十多岁该做些什么,将来才不会后悔?http://www.zhihu.com/question/20151457 读初中是为了上一个高中,读高中是为了上一个大学,读大学是为了工作.这么浅显的道理,可是 ...

  3. Unity C#基础之 反射反射,程序员的快乐

    反射反射,程序员的快乐 这句话想必大家都经常听过,基本上在绝大多数的框架和一些设计模式中都能看到反射的身影(MVC.IOC.AOP.O/RM), 反射:是.Net Framework提供的一个帮助类库 ...

  4. 3dsMax是什么?有什么功能、能做些什么?

    3dsMax是什么? 3DsMax定义:3D Studio Max,常简称为3ds Max,是Discreet公司开发的(后被Autodesk公司合并)基于PC系统的三维动画渲染和制作软件. 3DsM ...

  5. 实现量子计算,我们还需要做些什么?

    2020-03-31 18:21 导语:来自腾讯量子实验室的解读. 整理 | 蒋宝尚 2019年是量子计算占据新闻热点版面的一年,从1月份IBM公布全球首款商用量子计算原型机到9月份谷歌宣告在全球首次 ...

  6. 在对人的管理上,项目经理应该做些什么?

    在对人的管理上,项目经理应该做些什么? 2010-09-26 18:51:00 标签:企业管理 博客话题 人 管理 项目经理 [推送到技术圈] 版权声明:原创作品,如需转载,请与作者联系.否则将追究法 ...

  7. 80后游戏美术:这十年总在挣扎着做些更有意思的事儿

    写在前面:游戏和艺术之间的关系一直有点扑朔迷离,称游戏为"第九艺术"曾经是种流行:前八种的界定是否准确暂且不提,光是费尽心思想要挤进这个越来越长的列表里,似乎就已经说明了游戏的不自 ...

  8. Log4j史诗级漏洞,我们这些小公司能做些什么?

    事件背景 12月10日,看到朋友圈中已经有人在通宵修改.上线系统了.随即,又看到阿里云安全.腾讯安全部门发出的官方报告:"Apache Log4j2存在远程代码执行漏洞",且漏洞已 ...

  9. 腾讯云大学大咖分享 | 自然语言处理技术(NLP)究竟能做些什么?

    自然语言处理(Natural Language Processing,缩写作 NLP)是人工智能(AI)领域的一个重要分支,被广泛应用于聊天机器人.机器翻译和搜索引擎等场景.为帮助大家更好地理解NLP ...

  10. COVID-19席卷全球,看看GIS建模可视化能做些什么

    新冠疫情席卷全球,看看GIS建模可视化能做些什么 一.什么是GIS建模 简单来说,GIS建模是将时空要素及相关信息进行挖掘量化,得出结论以用作分析指导,通常情况下,结论会以可视化的方式呈现出来. 二. ...

最新文章

  1. JSP中 JSTL和EL标签的使用
  2. python语言程序设计基础第二版第七章答案-Python核心编程第二版 第七章课后答案...
  3. JavaScript语句模版
  4. GIT 中同时 push 代码到多个远程仓库
  5. 鸿蒙应用开发--组件
  6. [翻译]当jQuery遭遇CoffeeScript的时候——妙,不可言
  7. 三星java3倍拍照手机_最强安卓拍照手机!三星Note 8将采用双摄+三倍光学变焦
  8. 基于麻雀算法改进的LSTM分类算法-附代码
  9. Bad Rabbit
  10. python:双色球随机选号
  11. css 大于号 标签_css选择器 ~ (波浪号)、+(加号)、(大于号)的用法解析和举例...
  12. 涨知识!中国天眼一秒钟要用多少度电?
  13. 那些不能在一起吃的水果搭配
  14. 考研数学随笔(2)——微分积分关系,中值定理
  15. Ambiguous method overloading for method ****** 异常的解决办法
  16. 万众电子期刊在线阅读系统(awd复现)
  17. 效率倍增!4 个鲜为人知却功能强大的魔法命令!
  18. JQuery对元素拖拽排序,元素拖拽,JQuery拖拽
  19. 引入时间轴:动态图模型的共性与特征
  20. C#面向对象三大特性(重要)

热门文章

  1. Android安卓成品项目 购物商城系统源码apk
  2. 协方差,协方差矩阵,矩阵特征值
  3. 蛋白质相互作用系列:GN快速算法
  4. c语言 实习报告,计算机专业c语言实训报告范文
  5. 第二阶段--团队冲刺--第六天
  6. Code::Blocks 的配色方案
  7. web前端 day11今日大纲
  8. 【SpringBoot】之自定义 Filter 过滤器
  9. 基于Laravel 框架的内容管理系统
  10. 1小时、1天、1个月、一年的时间戳