一.什么是反射程序员
Reflection,中文翻译为反射。这是.Net中获取运行时类型信息的方式,编程
.Net的应用程序由几个部分:‘程序集(Assembly)’、‘模块(Module)’、‘类型(class)’组成,而反射提供一种编程的方式,让程序员能够在程序运行期得到这几个组成部分的相关信息,api
例如:Assembly类能够得到正在运行的程序集信息,也能够动态的加载程序集,以及在程序集中查找类型信息,并建立该类型的实例。数组
Type类能够得到对象的类型信息,此信息包含对象的全部要素:方法、构造器、属性等等,经过Type类能够获得这些要素的信息,而且调用之。ui
MethodInfo包含方法的信息,经过这个类能够获得方法的名称、参数、返回值等,而且能够调用之。诸如此类,this
还有FieldInfo、EventInfo等等,这些类都包含在System.Reflection命名空间下。spa
二. 关于程序集和命名空间的关系翻译
不少人对这个概念可能仍是很不清晰,对于合格的.Net程序员,有必要对这点进行澄清。设计
程序集是.NET应用程序执行的最小单元,编译出来的.dll和.exe都是程序集。 程序集和命名空间的关系不是一一对应,也不互相包含,一个程序集里面能够有多个命名空间,一个命名空间也能够在多个程序中存在,这样说可能有点模糊,举个例子:code
程序集A包含两个命名空间:
namespace N1 { public class AC1 {…} public class AC2 {…} } namespace N2 { public class AC3 {…} public class AC4{…} }
程序集B包含两个命名空间:
namespace N1 { public class BC1 {…} public class BC2 {…} } namespace N2 { public class BC3 {…} public class BC4{…} }
这两个程序集中都有N1和N2两个命名空间,并且各声明了两个类,这样是彻底能够的,而后咱们在一个应用程序中引用程序集A,那么在这个应用程序中,咱们能看到N1下面的类为AC1和AC2,N2下面的类为AC3和AC4。
接着咱们去掉对A的引用,加上对B的引用,那么咱们在这个应用程序下能看到的N1下面的类变成了BC1和BC2,N2下面也同样。 若是咱们同时引用这两个程序集,那么N1下面咱们就能看到四个类:AC一、AC二、BC1和BC2。
到这里,咱们能够清楚一个概念了,命名空间只是说明一个类型是那个族的,好比有人是汉族、有人是回族;而程序集代表一个类型住在哪里,好比有人住在北京、有人住在上海;那么北京有汉族人,也有回族人,上海有汉族人,也有回族人,这是不矛盾的。
上面咱们说了,程序集是一个类型居住的地方,那么在一个程序中要使用一个类,就必须告诉编译器这个类住在哪儿,编译器才能找到它,也就是说必须引用该程序集。
那么若是在编写程序的时候,也许不肯定这个类在哪里,仅仅只是知道它的名称,就不能使用了吗?答案是能够,这就是反射了,就是在程序运行的时候提供该类型的地址,而去找到它。
三.为何使用反射
有人会有疑问,程序所用的类既然能够事先写好,那么为何还要在程序运行的时候去生成,这样岂不是浪费系统资源。存在就是合理的,既然微软给咱们开发这项技术,确定是这个这个东西有需求,举个简单的例子:
如今要开发一个报表打印模块,有的企业要求数据以Excel报表,有的企业要求打印水晶报表等等,这个时候咱们就能够先定义一个接口,任何报表打印方法都必须实现这个接口:
public interface IReport { void StartPrint(); }
咱们经过配置文件能够加载对应的读取报表的类型,由于报表打印类都实现了IReport的接口,因此均可以经过反射的方式强转为该接口类型,这样就能够实现无需修改底层代码就能够实现打印不一样的数据报表,符合我i们程序设计的开闭原则,这个就是反射最经典的应用。
public static class Factory
{
//【1】读取配置文件
static string reportType = ConfigurationManager.AppSettings["ReportType"].ToString();
//【2】使用反射建立实现接口类的对象并以接口类型返回
public static IReport ChooseReportType()
{
return (IReport)Assembly.Load("UseFactory").CreateInstance("UseFactory." + reportType);
}
}
四.如何使用反射获取类型
获取类信息有两种方式:
第一种方式,获得实例化对象。 这个时侯我仅仅是获得这个实例对象,获得的方式也许是一个object的引用,也许是一个接口的引用,可是我并不知道它的确切类型,我须要了解,那么就能够经过调用System.Object上声明的方法GetType来获取实例对象的类型对象,好比在某个方法内,我须要判断传递进来的参数是否实现了某个接口,若是实现了,则调用该接口的一个方法。
public void Progress(object o) { Type objType = o.GetType(); if (objType.GetInterface("ITest") !=null) { //调用该接口的方法 } }
第二种获取类型的方法是经过Type.GetType以及Assembly.GetType方法,可是在使用该方法时咱们须要注意一些问题点。在程序集A.dll中须要反射程序集B.dll中的类型。若是使用稍有不慎,就会产生运行时错误。例如使用Type.GetType("BNameSpace.ClassName")在程序集A.dll获取程序集B.dll中的类型,就会返回Null。
关于跨程序集的反射,有两点须要注意:
一、若是使用typeof,编译能经过,则跨程序集的反射必定能够正常运行。能够说,typeof是支持强类型的。好比
Type supType = typeof(BNameSpace.SubSpace.Class);
若是当前程序集没有添加对EnterpriseServerBase.dll的引用,则编译会报错。
二、若是使用Type.GetType来进行反射的话,状况就复杂些。这是由于Type.GetType是非强类型的。Type.GetType的参数是一个string为类型的彻底限定名,若是当string表示的目标类型不在当前程序集中,则运行时Type.GetType会返回null。解决的办法是:首先加载目标程序集,而后再使用Assembly.GetType方法来获取类型。如:
Assembly asmb = Assembly.LoadFrom("EnterpriseServerBase.dll") ; Type supType = asmb.GetType("EnterpriseServerBase.DataAccess.IDBAccesser") ;
注意:当使用Type.GetType的时候,即便你添加了对EnterpriseServerBase.dll的引用,Type.GetType("EnterpriseServerBase.DataAccess.IDBAccesser")也会返回null,这是由于Type.GetType只会在当前程序集中进行类型搜索。
5.如何根据类型动态建立对象
第一种方式
public class Example { static void Main() { // Create an instance of the StringBuilder type using // Activator.CreateInstance. Object o = Activator.CreateInstance(typeof(StringBuilder)); // Append a string into the StringBuilder object and display the // StringBuilder. StringBuilder sb = (StringBuilder) o; sb.Append("Hello, there."); Console.WriteLine(sb); // Create an instance of the SomeType class that is defined in this // assembly. System.Runtime.Remoting.ObjectHandle oh = Activator.CreateInstanceFrom(Assembly.GetEntryAssembly().CodeBase, typeof(SomeType).FullName); // Call an instance method defined by the SomeType type using this object. SomeType st = (SomeType) oh.Unwrap(); st.DoSomething(5); } }
例二:根据有参数的构造器建立对象
namespace TestSpace { public class TestClass { private string _value; public TestClass(string value) { _value=value; } } } Type t = Type.GetType(“TestSpace.TestClass”); Object[] constructParms = new object[] {“hello”}; //构造器参数 TestClass obj = (TestClass)Activator.CreateInstance(t,constructParms);
把参数按照顺序放入一个Object数组中便可
第三种方式:
假如咱们并无加载对应.cs文件的程序集,那么咱们该如何简洁的建立对象类型呢:
object objCal =Assembly.LoadFrom("CalDLL.dll").CreateInstance("CalDLL.Calculator");
是否是很简洁,固然咱们也能够先获取对象的类型,再用Activator.CreateInstance 来建立对象,不过稍显麻烦:
Assembly objAssembly = Assembly.LoadFrom("CalDLL.dll"); Type objType = objAssembly.GetType("CalDLL.Calculator"); object objCal = Activator.CreateInstance(objType);
这样也能达到相同的效果。
6.如何获取方法以及动态调用方法
应用反射动态调用方法的示例以下:
using System; using System.Reflection; class Program { // Methods to get: public void MethodA(int i, int j) { } public void MethodA(int[] i) { } public unsafe void MethodA(int* i) { } public void MethodA(ref int r) {} // Method that takes an out parameter: public void MethodA(int i, out int o) { o = 100;} static void Main(string[] args) { MethodInfo mInfo; // Get MethodA(int i, int j) mInfo = typeof(Program).GetMethod("MethodA", BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Any, new Type[] { typeof(int), typeof(int) }, null); Console.WriteLine("Found method: {0}", mInfo); // Get MethodA(int[] i) mInfo = typeof(Program).GetMethod("MethodA", BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Any, new Type[] { typeof(int[]) }, null); Console.WriteLine("Found method: {0}", mInfo); // Get MethodA(int* i) mInfo = typeof(Program).GetMethod("MethodA", BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Any, new Type[] { typeof(int).MakePointerType() }, null); Console.WriteLine("Found method: {0}", mInfo); // Get MethodA(ref int r) mInfo = typeof(Program).GetMethod("MethodA", BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Any, new Type[] { typeof(int).MakeByRefType() }, null); Console.WriteLine("Found method: {0}", mInfo); // Get MethodA(int i, out int o) mInfo = typeof(Program).GetMethod("MethodA", BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Any, new Type[] { typeof(int), typeof(int).MakeByRefType() }, null); Console.WriteLine("Found method: {0}", mInfo); } }
关于GetMethod()的更多信息能够经过以下:
https://docs.microsoft.com/en-us/dotnet/api/system.type.getmethod?view=netframework-4.8
7.如何动态的获取属性信息:
下面的例子简单的说明了如何获取属性信息
void GetProperty() { Type objType = typeof(string); PropertyInfo[] obj = objType.GetProperties(); if (obj !=null) { foreach (var item in obj) { this.textBox1.Text = this.textBox1.Text + item.Name.ToString() + "\r\n"; } } }