C#回顾 –5.反射

 


简单来说,反射提供这样几个能力:
1、查看和遍历类型(及其成员)的基本信息和程序集元数据(metadata);
2、迟绑定(Late-Binding)方法和属性。
3、动态创建类型实例(并可以动态调用所创建的实例的方法、字段、属性)。

1.获取类型的相关信息

public  abstract  class Type : MemberInfo, _Type, IReflect


使用 System.Type 访问元数据

类型的名称是什么( Type.Name )?
类型是public吗( Type.IsPublic )?
类型的基类型是什么( Type.BaseType )?
类型支持任何接口吗( Type.GetInterfaces() )?
类型是哪个程序集中定义的( Type.Assembly )?
类型的属性( Type.GetProperties() )、方法( Type.GetMethods() )、字段( Type.GetFields() )是什么?
都有什么特性在修饰一个类型( Type.GetCustomAttributes() )?

获取Type对象的引用方法:
1. object.GetType()
2. typeof() 

1.GetType()

  object 包含一个 GetType() 方法。所以所有类型都包含。
  1. DateTime dateTime = new DateTime();  
  2.   
  3. Type type = dateTime.GetType();  
  4. Console.WriteLine("属性:");  
  5. foreach (System.Reflection.PropertyInfo property in type.GetProperties())  
  6. {  
  7.     Console.WriteLine(property.Name);  
  8. }  
 调用 GetType() 的关键在于获得一个对象实例,但有些时候不能获取,例如,静态类就无法实例化,所以没法调用。

2.typeof()

  1. Type type = typeof (DateTime);  

2.获取其他类型的相关信息(命名空间名、全名、是否是抽象、是否是类、、、等等)

命名空间名、全名、是否是抽象、是否是类、、、等等
  1. Type type = typeof (DateTime);  
  2. Console.WriteLine("命名空间名称:" + type.Namespace);  
  3. Console.WriteLine("直接基类型:" + type.BaseType);  
  4. Console.WriteLine("全名:" + type.FullName);  
  5. Console.WriteLine("是抽象类型:" + type.IsAbstract);  
  6. Console.WriteLine("是类:" + type.IsClass);  

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

  1. DateTime dateTime = new DateTime();  
  2. Type type = typeof(DateTime);  
  3. var mers = type.GetMembers();//获取Type对象的所有公有成员(返回值是MemberInfo类型集合)             
  4. foreach (var mer in mers)  
  5. {  
  6.     Console.WriteLine("【" + mer.MemberType.ToString() + "】:" + mer.Name);  
  7. }  

MemberTypes


  MemberType 属性的返回类型为 MemberTypes ,这是一个枚举,它定义了用于表示不同成员的类型值。因此可以通过检查 MemberType 属性来确定成员的类型,例如,在 MemberType 属性的值为 MemberTypes.Method 时,该成员为方法。

 MemberType 所能包含的成员类型有哪些呢?


 
  1. if (mer.MemberType == MemberTypes.Method)  


注意:

其中 MemberInfo 的属性 DeclaringType 返回的是这个属性定义的类型,
ReflectedType 返回的是获取这个属性的对象类型
先来个类:
  1. public class Dog  {  }  

输出:
  1. Type type = typeof(Dog);  
  2. var mers = type.GetMembers();//获取Type对象的所有公有成员   
  3.   
  4. foreach (var mer in mers)  
  5. {  
  6.     if (mer.Name == "Equals")  
  7.     {                  
  8.         Console.WriteLine("【" + mer.MemberType + "】:" + mer.Name);  
  9.     }  
  10. }  

 

4.动态调用方法

  1. public class TClass  
  2. {  
  3.     public void fun(string str)  
  4.     {  
  5.         Console.WriteLine("我是fun方法,我被调用了。" + str);  
  6.     }  
  7.     public void fun2()  
  8.     {  
  9.         Console.WriteLine("我是fun2方法,我被调用了。");  
  10.     }  
  11.   
  12.     public static void fun3()  
  13.     {  
  14.         Console.WriteLine("我是fun3静态方法,我被调用了");  
  15.     }  
  16. }  

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


调用带参实例方法fun
  1. Type T1 = typeof(TClass);  

  2. T1.InvokeMember(  

  3.     "fun"//要被动态调用的方法名  

  4.     System.Reflection.BindingFlags.InvokeMethod,  //是一个枚举,表示是调用一个方法

  5.     null//是Binder,传的是null,使用默认值,  

  6.     new TClass(),//传如实例对象(调用实例方法时)或者Type对象(调用静态方法时)

  7.     new string[] { "test" });//要传给被调用发的参数数组。  


 调用无参实例方法fun2
  1. Type T2 = new TClass().GetType();  

  2. T2.InvokeMember("fun2",   

  3.     System.Reflection.BindingFlags.  

  4.     InvokeMethod,   

  5.     null,   

  6.     new TClass(),   

  7.     null);  

 调用静态方法
  1. Type T3 = typeof(TClass);  

  2. T3.InvokeMember("fun3",   

  3.     System.Reflection.BindingFlags.InvokeMethod,   

  4.     null,   

  5.     T1,   

  6.     null);  


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


  1. Type T1 = typeof(TClass);  
  2. T1.GetMethod("fun", BindingFlags.Instance | BindingFlags.Public).Invoke(new TClass(), new string[] { "testfun1" });  
  3. T1.GetMethod("fun2", BindingFlags.Instance | BindingFlags.Public).Invoke(new TClass(), null);  
  4. T1.GetMethod("fun3", BindingFlags.Static | BindingFlags.Public).Invoke(T1, null);  
 使用其实和上面的方式一区别不大。

3.真正的全动态调用


  1. Console.WriteLine("请输入对象类名");  
  2. string className = Console.ReadLine(); //这里一定要输入命名空间(动态调用.TClass)不然找不到  
  3. Console.WriteLine("请输入要执行的方法名");  
  4. string funName = Console.ReadLine();  
  5. Type T1 = Type.GetType(className);  
  6.   
  7. ConstructorInfo ci = T1.GetConstructors()[0]; //获取构造函数   
  8. var obj = ci.Invoke(null);//实例化构造函数  
  9.   
  10. T1.InvokeMember(funName, BindingFlags.InvokeMethod, null, obj, null);  
当然,这个代码只能只是 fun2 ,因为上面的传参写死了。
 

5.动态构造对象

先定义一个对象
  1. public class TClass  

  2. {  

  3.     public string Name { getset; }  

  4.     public TClass()  

  5.     {  

  6.         Console.WriteLine("构造函数被执行了。。");  

  7.     }  

  8.     public TClass(string str)  

  9.     {  

  10.         Console.WriteLine("有参构造函数被执行了。。" + str);  

  11.     }  

  12. }  


动态构造对象
  1. //动态构造对象,方式一  

  2. Assembly asm = Assembly.GetExecutingAssembly();  

  3. TClass obj = (TClass)asm.CreateInstance("动态调用.tclass"true);//true:不区分大小写  

  4.   

  5. //动态构造对象,方式二  

  6. ObjectHandle handler = Activator.CreateInstance(null" 动态调用.TClass");//null:当前程序集  

  7. obj = (TClass)handler.Unwrap();  

  8.   

  9. //动态构造对象,方式三(构造有参构造函数)  

  10. Assembly asm2 = Assembly.GetExecutingAssembly();  

  11. obj = (TClass)asm2.CreateInstance("动态调用.tclass"true, BindingFlags.Default, nullnew string[] { "test" }, nullnull);//true:不区分大小写  



 

6.获取和修改属性

使用上面的类
  1. var obj = new TClass();  

  2. obj.Name = "张三";  

  3. Type type = typeof (TClass);  

  4. //获取属性  

  5. var name = type.InvokeMember("Name", BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance,  

  6.     null, obj, null);  

  7. Console.WriteLine("name:"+obj.Name);  

  8.   

  9. //设置属性  

  10. type.InvokeMember("Name", BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance, null,  

  11.                       obj, new object[] { "新属性(李四)" });  

  12. Console.WriteLine(obj.Name);  


 

7.从程序集中获得类型

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


  1. Assembly ass = Assembly.GetExecutingAssembly();  

  2. Console.WriteLine("当前所在程序集名:" + ass.ManifestModule.Name);  

  3. Console.WriteLine("当前所在程序集路径:" + ass.Location);  


 

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


从程序集中获得类型,这个应该是我们平时用得比较多。如我们所谓的依赖注入和控制反转就用到了通过反射从程序集中获取类型。

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

  1. Assembly asm1 = Assembly.LoadFrom("动态调用.exe");  

  2. Assembly asm2 = Assembly.Load("动态调用");


两者区别:

  1. Assembly asm = Assembly.LoadFrom("动态调用.exe");//需要加后缀,可以指定路径,如下面的    

  2. //Assembly asm1 = Assembly.LoadFrom(@"F:\TestCode\反射与特性\动态调用\bin\Debug\动态调用.exe");  

  3.   

  4. Assembly asm2 = Assembly.Load("动态调用");//无需加后缀,不可以指定路径    

  5. //Assembly asm3 = Assembly.Load(@"F:\TestCode\反射与特性\动态调用\bin\Debug\动态调用.exe");//这里会报错    

  6. //使用Load可以加载当前程序bin目录行下的程序集或者系统程序集    

  7.   

  8. //这里TClass可以是一个接口,那么可以在外面的dll任意实现了。      

  9. TClass obj = (TClass)asm2.CreateInstance("动态调用.tclass"true);//true:不区分大小写    

  10. Console.WriteLine(obj.Name);  

这样带来的功能是非常强大的。如 我们在没有引用程序集的情况下,也可以使用到程序外的程序集。我们还可以根据不同情况引用不同的程序集。我们甚至还可以通过配置文件来直接配置代码运行时应该加载哪个dll,运行哪个dll中的哪个实现方法。(下篇在讲依赖注入的时候会讲到,同学们继续关注哦~)
从上所知,反射不是某一个概率,而是一类操作的统称。或者说是某些能力的统称。 感觉不好回答反射到底是什么,只能说反射能干什么。它能动态创建对象、动态调用对象方法、动态读取和设置属性和字段、它能动态加载程序外的dll。总的感觉就是大多数都是跟“动态”扯上了关系。


农码一生 《二、什么是反射、反射可以做些什么》









posted @ 2016-11-25 09:29  【唐】三三  阅读(425)  评论(0编辑  收藏  举报