反射入门篇 (二)
上一篇文章(http://www.cnblogs.com/Mervin/archive/2013/03/20/reflection-1.html)中对反射有了简单的认识和了解,反射的功能还有很多,本篇文章将继续和大家一起探讨反射相关的基础知识。
上篇提到反射用到的两个主要的类,System.Type和Assembly。Assembly提供程序集的相关操作,Type类则用于在获取程序集中某个类型后的一些相关操作,本篇将着重介绍这两个类的一些常用操作。
首先来介绍Assembly类,要使用Assembly类,首先需要引入System.Reflection命名空间: System.Reflection 命名空间包含通过检查托管代码中程序集、模块、成员、参数和其他实体的元数据来检索其相关信息的类型。这些类型还可用于操作加载类型的实例,例如挂钩事件或调用方法。若要动态创建类型,请使用 System.Reflection.Emit 命名空间。(Emit相关功能将在下一篇功能中详细学习)。Assembly表示一个程序集,它是一个可重用、无版本冲突并且可自我描述的公共语言运行时应用程序构造块。简单的说Assembly就是一个程序集,Assembly类里有很多的属性和方法,可以获取到程序集的相关信息,下面列举几个常用的属性和方法(详细的属性方法可参见MSDN)。
| 属性 | 说明 |
|---|---|
| FullName | 获取程序集的显示名称。 |
| GlobalAssemblyCache | 获取一个值,该值指示程序集是否是从全局程序集缓存加载的。 |
| IsDynamic | 获取一个值,该值指示当前程序集是否是通过使用反射发出在当前进程中动态生成的。 |
| IsFullyTrusted | 获取一个值,该值指示当前程序集是否是以完全信任方式加载的。 |
| Location | 获取包含清单的已加载文件的路径或 UNC 位置。 |
| ManifestModule | 获取包含当前程序集清单的模块。 |
| 方法 | 说明 |
|---|---|
| Load(String) | 通过给定程序集的名称来加载程序集。 |
| LoadFrom(String) | 通过给定程序集的路径来加载程序集。 |
| ToString | 返回程序集的全名,即显示名称。 |
| GetExecutingAssembly | 获得当前正在执行代码所属的程序集 |
| GetTypes | 获得程序集中的所有类型,返回Type数组。 |
| GetType(String) | 获取程序集实例中具有指定名称的Type对象 |
| GetType(String,Boolean) | 获取程序集实例中具有指定名称的Type对象,后一个参数指明在未找到此类型时是否引发异常。 |
| GetType(String,Boolean,Boolean) | 同上,最后一个参数指明对于给定的名称是否区分大小写。 |
System.Type 则为我们提供了在获取程序集中某个类型后的后续操作,本例中用到的Type类的一些方法有:
| 方法 | 说明 |
|---|---|
| GetMethod(String) | 搜索具有指定名称的公共方法。 |
| GetMember(String) | 搜索具有指定名称的公共成员。 |
| GetProperty(String) | 搜索具有指定名称的公共属性。 |
| GetEvent(String) | 搜索具有指定名称的公共事件。 |
| GetField(String) | 搜索具有指定名称的公共字段。 |
| GetInterface(String) | 搜索具有指定名称的接口。 |
上面的方法都有对应的复数方法 如:GetMethods,返回对应Type类里的方法数组 MethodInfo[]
这里要注意的是这些方法默认只能搜索到公共的成员信息,这一点在下面的示例中也可以体现出来
如果需要搜索私有成员可以用对应的重载方法 Get*(String,BindingFlags)
如:GetMember(String,BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
此外,Get*方法还能够支持类似的模糊搜索功能,如:下面的方法将返回名字以“th"开头的实例成员
MemberInfo[] mio = myType.GetMember("th*",BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
BindingFlags类型的参数是一个枚举类型,常用的一些值如下:
![]() |
Default | |
![]() ![]() |
IgnoreCase | 指定当绑定时不应考虑成员名的大小写。 |
![]() ![]() |
DeclaredOnly | 指定只应考虑在所提供类型的层次结构级别上声明的成员。不考虑继承成员。 |
![]() ![]() |
Instance | 指定实例成员将包括在搜索中。 |
![]() ![]() |
Static | 指定静态成员将包括在搜索中。 |
![]() ![]() |
Public | 指定公共成员将包括在搜索中。 |
![]() ![]() |
NonPublic | 指定非公共成员将包括在搜索中。 |
![]() ![]() |
FlattenHierarchy | 指定应返回层次结构上的公共静态成员和受保护的静态成员。不返回继承类中的私有静态成员。静态成员包括字段、方法、事件和属性。不返回嵌套类型。 |
![]() ![]() |
InvokeMethod | 指定要调用一个方法。它不能是构造函数或类型初始值设定项。 |
![]() ![]() |
CreateInstance | 指定“反射”应该创建指定类型的实例。调用与给定参数匹配的构造函数。忽略提供的成员名。如果未指定查找类型,将应用 (Instance |Public)。调用类型初始值设定项是不可能的。 |
![]() ![]() |
GetField | 指定应返回指定字段的值。 |
![]() ![]() |
SetField | 指定应设置指定字段的值。 |
![]() ![]() |
GetProperty | 指定应返回指定属性的值。 |
![]() ![]() |
SetProperty | 指定应设置指定属性的值。对于 COM 属性,指定此绑定标志与指定 PutDispProperty 和 PutRefDispProperty 是等效的。 |
接下来通过一个示例来看下这些属性和方法的使用:
首先我们先创建一个解决方案,包含两个项目,一个是类库,一个控制台应用程序:

相关代码:
MultiThreadDemo.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 7 namespace ClassLibrary1 8 { 9 public class MultiThreadDemo 10 { 11 private Thread thread1 = null; 12 private Thread thread2 = null; 13 private List<int> lstInts = null; 14 private event EventHandler ehRemoveCompleted; 15 private static object lockremove = new object(); 16 17 public MultiThreadDemo() 18 { 19 ehRemoveCompleted += new EventHandler(MultiThreadDemo_ehRemoveCompleted); 20 21 lstInts = new List<int>(); 22 23 for (int i = 0; i < 10; i++) 24 { 25 lstInts.Add(i); 26 } 27 28 thread1 = new Thread(Run); 29 thread1.Name = "线程1 "; 30 thread2 = new Thread(Run); 31 thread2.Name = "线程2 "; 32 } 33 34 public string GetString 35 { 36 get; 37 set; 38 } 39 40 public void DoRemove() 41 { 42 thread1.Start(); 43 thread2.Start(); 44 } 45 46 private void Run() 47 { 48 while (true) 49 { 50 Monitor.Enter(this); 51 if (lstInts.Count > 0) 52 { 53 Console.WriteLine(string.Format("{0}:移除了 {1} .", Thread.CurrentThread.Name, lstInts[0])); 54 lstInts.RemoveAt(0); 55 } 56 57 Monitor.Exit(this); 58 59 if (lstInts.Count == 0) 60 { 61 ehRemoveCompleted(this, new EventArgs()); 62 } 63 64 Thread.Sleep(3); 65 } 66 } 67 68 private void MultiThreadDemo_ehRemoveCompleted(object sender, EventArgs e) 69 { 70 Console.WriteLine("Completed!"); 71 thread1.Abort(); 72 thread2.Abort(); 73 Console.ReadLine(); 74 } 75 76 } 77 }
Program.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 using System.Reflection; 7 using System.Reflection.Emit; 8 namespace MultiThread 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 //此处得到当前正在运行代码的程序集所在位置 15 string apppath = Assembly.GetExecutingAssembly().Location; 16 apppath = apppath.Substring(0, apppath.LastIndexOf("\\") + 1); 17 //根据程序集路径加载程序集,也可根据名称加载 Assembly.Load("ClassLibrary1"); 18 Assembly ass = Assembly.LoadFrom(apppath + "ClassLibrary1.dll"); 19 //获得程序集的相关属性信息并输出 20 Console.WriteLine("FullName:" + ass.FullName); 21 Console.WriteLine("GlobalAssemblyCache:" + ass.GlobalAssemblyCache); 22 Console.WriteLine("IsDynamic:" + ass.IsDynamic); 23 Console.WriteLine("IsFullyTrusted:" + ass.IsFullyTrusted); 24 Console.WriteLine("Location:" + ass.Location); 25 Console.WriteLine("ManifestModule:" + ass.ManifestModule); 26 Console.WriteLine("PermissionSet:" + ass.PermissionSet); 27 Console.WriteLine("ReflectionOnly:" + ass.ReflectionOnly); 28 Console.WriteLine("SecurityRuleSet:" + ass.SecurityRuleSet); 29 Console.WriteLine("ImageRuntimeVersion:" + ass.ImageRuntimeVersion); 30 Console.WriteLine("HostContext:" + ass.HostContext); 31 Console.WriteLine("Evidence:" + ass.Evidence); 32 Console.WriteLine("EscapedCodeBase:" + ass.EscapedCodeBase); 33 Console.WriteLine("EntryPoint:" + ass.EntryPoint); 34 Console.WriteLine("CodeBase:" + ass.CodeBase); 35 36 //这里从程序集中获得某个类型 37 Type t = ass.GetType("ClassLibrary1.MultiThreadDemo", false, true); 38 39 MethodInfo[] mis = t.GetMethods();//方法 40 MemberInfo[] mio = t.GetMembers();//成员 41 EventInfo[] ei = t.GetEvents();//事件 42 FieldInfo[] fi = t.GetFields(); //字段 43 44 //方法 45 ParameterInfo[] paras = null; 46 Console.WriteLine("方法:-----------------"); 47 foreach (MethodInfo mi in mis) 48 { 49 Console.WriteLine(string.Format("方法名:{0},返回类型:{1} ", mi.Name, mi.ReturnType)); 50 paras = mi.GetParameters();//方法的参数获取 51 foreach (ParameterInfo p in paras) 52 { 53 Console.Write(string.Format("参数名:{0},类型:{1}", p.Name, p.ParameterType)); 54 } 55 } 56 57 //成员 58 Console.WriteLine("成员信息:--------------------"); 59 foreach (MemberInfo me in mio) 60 { 61 Console.WriteLine(string.Format("{0},{1}", me.Name, me.MemberType)); 62 } 63 64 //事件 65 Console.WriteLine("事件信息:--------------------"); 66 foreach (EventInfo ev in ei) 67 { 68 Console.WriteLine(string.Format("{0},{1}", ev.Name, ev.EventHandlerType)); 69 } 70 71 //字段 72 Console.WriteLine("字段信息:--------------------"); 73 foreach (FieldInfo fo in fi) 74 { 75 Console.WriteLine(string.Format("{0},{1}", fo.Name, fo.MemberType)); 76 } 77 78 //通过Activator创建示例 79 object demo = Activator.CreateInstance(t); 80 //得到其中某个方法 81 MethodInfo methoddo = t.GetMethod("DoRemove"); 82 methoddo.Invoke(demo, null);//调用方法 83 //得到某个属性 84 PropertyInfo proinfo = t.GetProperty("GetString"); 85 //属性赋值 86 proinfo.SetValue(demo, "qiangmervin", null); 87 //获取属性值 88 Console.WriteLine(proinfo.GetValue(demo, null)); 89 Console.ReadLine(); 90 } 91 92 } 93 }
注:将ClassLibrary1类库编译后的dll程序集文件,拷贝到控制台应用程序的Debug目录下,运行后可以看到如下结果:

此例,主要学习了反射用到的一些相关基础知识,下篇将介绍反射中Emit的使用,反射在提供了程序集动态加载方便的同时也存在性能的问题,而使用Emit将能有效的降低反射带来的性能损耗。 每天进步一点点,坚持!




浙公网安备 33010602011771号