c#反射整理一:API
仅作为个人整理,防止他人博客关闭导致收藏的页面drop掉
反射介绍
http://www.cnblogs.com/guowenhui/archive/2011/12/18/2291552.html
在程序中,当我们需要动态的去加载程序集的时候(将对程序集的引用由编译时推移到运行时),反射是一种很好的选择。反射为.NET类型提供了高度的动态能力,包括:元数据的动态查询、绑定与执行、动态代码生成。常用的反射类型包含在System.Reflection和System.Reflection.Emit,反射包括程序集反射、类型反射、接口反射、类型成员反射。
编译时加载程序集
下面先从一个简单的例子说起,假如我们有一个Point类如下所示:
using System;
public class Point
{public int x;public int y;public void Print(){Console.WriteLine("[{0},{1}]",x,y);}
}
先将其编译成Point.dll文件,然后我们需要在Reflect.cs文件中用到这个类型:
代码如下:
using System;
public class Reflect
{public static void Main(){Point p=new Point();p.x=100;p.y=200;p.Print();}
}
然后我们编译这个文件,会发现抛出异常,告诉我们不存在Point类型,也就是说Point是一个未知类型,编译器根本不知道它,那怎么办了,这里我们需要在编译时对Point程序集进行引用,这样才能让编译器知道它,在很多应用程序开发框架中在我们编译项目的时候框架会自动进行程序集的引用,但是在这里我们的编译工作是手动进行的,因此我们也需要手动的引用程序集。下面是编译命令:
csc /r:Point.dll Reflect.cs
这样我们在编译Reflect.cs文件的过程中就实现了对Point程序集的引用,这样编译器就识别了程序集中Point类型信息。其实任何类型信息的使用都需要有程序集的引用,不然的话编译器会不认识这个类型,编译就无法通过。我们可能会想,我们在很多时候进行编译的时候并没有对哪个程序集进行引用么?不是没有,而是很多预定义的类型信息编译器会默认帮我们进行引用,不需要我们显示的进行引用,比如mscorlib程序集,它是.NET一个核心程序集,里面包含了很多常用的预定义的类型信息,因此C#编译器在编译的时候会默认对它进行引用,可能我们会想,我们怎么知道一段代码在编译的过程中它到底都引用了哪些程序集了,这里就需要借助于元数据,下面我们通过ildasm工具查看了编译好的Reflect.exe程序集中的元数据,我们只截取了其中一段进行说明。
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly extern Point
{
.ver 0:0:0:0
}
从这段元数据中我们可以看出,这个程序集中包含了对Point程序集和mscorlib程序集的引用。
利用反射实现延迟加载程序集
在上面所举的例子中,所有对程序集的引用都是在编译时进行的,因此效率会比较高。但是在某些特定的情况下,我们需要对程序集进行延迟加载,即将对程序集的引用由编译时推移到运行时,反射是一种很好的选择,在最开始我们讲过,反射为.NET类型提供了高度的动态能力,包括:元数据的动态查询、绑定与执行、动态代码生成,这些功能的实现都离不开元数据。下面我们看看反射的具体实现。
using System;
using System.Reflection;class Test
{public static void Main(string[] args){string assemblyName=args[0];string typeName=args[1];string fieldName1=args[2]; string fieldName2=args[3];string methodName=args[4];Assembly assembly=Assembly.Load(assemblyName); //手动加载程序集Type type=assembly.GetType(typeName); //获取程序集中的类型//查询MemberInfo[] mis=type.GetMembers(); //获取类型中的成员信息for(int i=0;i
这里我们在编译这段代码的时候并没有对Point程序集进行引用,而是将其推移到了运行时。在这段代码中Point类型并不存在,因此我们并不能用Point类型来创建对象实例。但是下面的几个操作是针对于对象的实例成员进行的,他们需要依附于对象的实例,因此我们在这里根据在运行时加载进来的类型信息创建了一个对象,但是编译时类型信息是未知的。我们可以通过查看它的元数据得知它编译后并没有对Point程序集进行加载。
那么运行时怎么对程序集进行加载了,这里我们就需要将需要加载的程序集的信息在入口点函数中以参数的形式传递进来,下面是运行时的命令:

在这段命令中我们指出了运行时需要加载的程序集的名称和其中的类型的名称以及类型中的成员信息。由于类型信息在编译时是未知的,因此我们并不清楚在运行时会加载进来什么样的类型信息(需要通过传入的参数确定)。也就是说我们现在所进行的一些操作是针对一个不确定的类型的(如果我们事先并不了解Point的类型信息)。而在上面的代码中我们之所以能够对加载进来类型信息进行这样的处理,是因为我们事先已经对Point类型信息有所了解了,因此我们遵循了这样一个隐式的约定来对类型进行操作。但是在很多情况下我们是并不了解未来加载进来的类型信息它所遵循的约定。因此在运行时,反射需要做很多的校验工作,也就是说把本来应该在编译时做的校验工作都推移到了运行时,比如参数类型的兼容性以及所调用的方法是实例方法还是静态方法,因此反射的效率比较低。
利用接口提高反射效率
那么怎么样才能提高反射的效率了,很简单,我们需要明确约定,也就是说只有满足了约定信息的类型才能被加载进来,也因此我们对类型成员的处理必须是满足了这一约定的,这样双方都有了一个共同的约定。那么我们用什么来实现这一约定了,当然需要用到接口了。
我们对上面的例子进行改进,通过对Point类型的观察,我们可以得到这样一个接口,我们先看接口的实现,并将其编译为IPoint.dll文件。
using System;
public interface IPoint
{public int X{set;get;}public int Y{set;get;}void Print();
}
下面我们来实现Point类,并对其进行编译,注意:在编译时确保对IPoint.dll的引用。
using System;
public class Point :IPoint
{private int x;private int y;public int X{set{this.x=value;}get{return x;}}public int Y{set{this.y=value;}get{return y;}}public void Print(){Console.WriteLine("[{0},{1}]",this.X,this.Y);}}
下面我们再来看Reflect类的实现,在对其进行编译时同样需要对IPoint.dll进行引用。
using System;
using System.Reflection;class Test
{public static void Main(string[] args){string assemblyName=args[0];string typeName=args[1];Assembly assembly=Assembly.Load(assemblyName); //手动加载程序集Type type=assembly.GetType(typeName); //获取程序集中的类型IPoint obj=(IPoint)Activator.CreateInstance(type); //通过接口创建对象实例obj.X=100;obj.Y=200;obj.Print(); }}
最后运行编译后的Reflect.exe文件,运行时同样需要传入程序集信息和类型信息。这里我们看到Point程序集和Reflect程序集在编译时都对IPoint程序集进行了引用,因此在Reflect程序集中,虽然在编译时并没有Point类型信息,但是有IPoint接口信息,因此我们通过这个接口很方便的实现了我们需要的操作,只要未来加载进来了类型是实现了IPoint接口的就可以了,这样在运行时就不需要进行大量的校验工作了,这些工作都还原到了编译时,因此使用接口来实现反射也大大提高了反射的性能。
System.Type.GetType()与Object.GetType()与typeof比较
原文:https://www.cnblogs.com/zmztya/p/7085939.html
//运算符,获得某一类型的 System.Type 对象。
Type t = typeof(int);
//方法,获取当前实例的类型。
int i = 10;
Console.WriteLine(i.GetType());
//区别
Typeof()是运算符而GetType是方法
GetType()是基类System.Object的方法,因此只有建立一个实例之后才能被调用(也就是创建实例)
Typeof()的参数只能是lint,string,类,且不能是实例
得到结果的区别
(1)Typeof():得到一个class的Type
(2)GetType():得到一个class实例的Type
System.Type.GetType()的使用
Type type = System.Type.GetType("ConsoleApplication1.child");
Type type1 = System.Type.GetType("System.Int32");
Object.GetType()的小案例
public class Student
{
public Student()
{
}
public virtual string Id { get; set; }
public virtual string StudentNo { get; set; }
public virtual string Address { get; set; }
}
public class StudentDTO
{
public StudentDTO()
{
}
public virtual string Id { get; set; }
public virtual string StudentNo { get; set; }
public virtual int TeacherId { get; set; }
}
//对student对象赋值
Student student = new Student();
student.Id = Guid.NewGuid().ToString();
student.Name = "张三";
student.Address = "福建";
//将student的值赋予studentdto
StudentDTO studentDTO = new StudentDTO();
studentDTO.Id = student.Id;
studentDTO.Name = student.Name;
改进:若是student的属性过多,那么可以通过此方法减少许多代码
foreach (var item in student.GetType().GetProperties()) //返回Student的所有公共属性
{
var value = item.GetValue(student, null); //返回属性值
var setobj = studentDTO.GetType().GetProperty(item.Name); //搜索具有指定属性名称的公共属性
if (value != null && setobj != null)
{
setobj.SetValue(studentDTO, value, null);
}
}
Assembly.Load、LoadFrom与LoadFile进阶
原文:https://www.cnblogs.com/zagelover/articles/2726034.html
关于.NET中的反射,常用的有三个方法:
Assembly.Load()
Assembly.LoadFrom()
Assembly.LoadFile()
下面说说这三个方法的区别和一些细节问题
1. Assembly.Load()
简介
Load()方法接收一个String或AssemblyName类型作为参数,这个参数实际上是需要加载的程序集的强名称(名称,版本,语言,公钥标记)。例如.NET 2.0中的FileIOPermission类,它的强名称是:
System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
对于弱命名的程序集,则只会有程序集名称,而不会有版本,语言和公钥标记。如 TestClassLibrary
细节
- CLR内部普遍使用了Load()方法来加载程序集,在Load()方法的内部,CLR首先会应用这个程序集的版本绑定重定向策略,接着在GAC中查找目标程序集。如果GAC中没有找到,则会在应用程序目录和子目录中寻找(应用配置文件的codebase所指定的位置)。
- 如果希望加载弱命名程序集,Load()方法就不会去GAC中查找。
- 当Load()找到目标程序集时,就会加载它,并返回一个相应Assembly对象的引用。
- 当没有找到程序集时,会抛出System.IO.FileNotFoundException异常。
- 当存在特定CPU架构的程序集时,CLR会优先加载当前架构的程序集(例如x86版本优先于IL中立版本)
- 如果希望强迫加载某个架构版本的程序集,需要在强名称中加以指定。ProcessorArchitecture可以为x86 IA64 AMD64或MSIL,当然还有None
- Load方法与Win32函数中的LoadLibrary方法等价
2. Assembly.LoadFrom()
简介
LoadFrom()方法可以从指定文件中加载程序集,通过查找程序集的AssemblyRef元数据表,得知所有引用和需要的程序集,然后在内部调用Load()方法进行加载。
Assembly.LoadFrom(@"C:\ABC\Test.dll");
细节
- LoadFrom()首先会打开程序集文件,通过GetAssemblyName方法得到程序集名称,然后关闭文件,最后将得到的AssemblyName对象传入Load()方法中
- 随后,Load()方法会再次打开这个文件进行加载。所以,LoadFrom()加载一个程序集时,会多次打开文件,造成了效率低下的现象(与Load相比)。
- 由于内部调用了Load(),所以LoadFrom()方法还是会应用版本绑定重定向策略,也会在GAC和各个指定位置中进行查找。
- LoadFrom()会直接返回Load()的结果——一个Assembly对象的引用。
- 如果目标程序集已经加载过,LoadFrom()不会重新进行加载。
- LoadFrom支持从一个URL加载程序集(如"http://www.abc.com/test.dll"),这个程序集会被下载到用户缓存文件夹中。
- 从URL加载程序集时,如果计算机未联网,LoadFrom会抛出一个异常。如果IE被设置为“脱机工作”,则不会抛出异常,转而从缓存中寻找已下载的文件。
3. Assembly.LoadFile()
简介
LoadFile()从一个指定文件中加载程序集,它和LoadFrom()的不同之处在于LoadFile()不会加载目标程序集所引用和依赖的其他程序集。您需要自己控制并显示加载所有依赖的程序集
细节
- LoadFile()不会解析任何依赖
- LoadFile()可以多次加载同一程序集
- 显式加载依赖程序集的方法是,注册AppDomain的AssemblyResolve事件
------------------------------------------------------------------------------------------------------------------------------------------
一些关于C#反射的知识,估计也就最多达到使用API的程度,至于要深入了解,以现在的水平估计很难做到,所以下面此篇文章,以作为一个阶段的总结。
对于反射的总结,我想从以下几个方面展开,首先是反射程序集,模块,类的成员以及成员的一些信息;接下来就是动态调用类的成员方法;第三个方面就动态产生程序集,模块和类以及类的成员。好了,现在就让我们从反射各种信息开始吧
在C#中,我们要使用反射,首先要搞清楚以下命名空间中几个类的关系:
System.Reflection命名空间
(1) AppDomain:应用程序域,可以将其理解为一组程序集的逻辑容器
(2) Assembly:程序集类
(3) Module:模块类
(4) Type:使用反射得到类型信息的最核心的类
他们之间是一种从属关系,也就是说,一个AppDomain可以包含N个Assembly,一个Assembly可以包含N个Module,而一个Module可以包含N个Type.
AppDomain这个类我们等下再来讲解。我们先关注Assembly个类
在程序中,如果我们要动态加载一个程序集怎么办呢?有几种方式可以使用,分别是Load、LoadFrom和LoadWithPartialName三个Assembly的静态方法.
先来讲解Assembly.Load方法,该方法会有多个重载版本,其中一个就是提供程序集的详细信息,即程序集的标识,包括程序集的名称,版本,区域信息,公有密钥标记,全部都是以一个字符串的形式提供,例如:"MyAssembly,Version=1.0.0.0,culture=zh-CN,PublicKeyToken=47887f89771bc57f”.
那么,使用Assembly.Load加载程序集的顺序是怎样的呢?首先它会去全局程序集缓存查找,然后到应用程序的根目录查找,最后会到应用程序的私有路径查找。
当然,如果你使用的是弱命名程序集,也即只给出程序集的名称,那么这个时候,CLR将不会在程序集上应用任何安全或者部署策略,而且Load也不会到全局缓存程序集中查找程序集。
测试加载弱命名程序集的例子如下:
(1) 新建一个控制台应用程序的工程,同时勾选创建解决方案
(2) 在解决方案中新建一个类库的项目,随便写一个类和一个方法
(3) 在控制台项目中,首先不添加引用,直接在Main方法中添加如下代码:
Assembly assembly = Assembly.Load("MyAssembly");
if (assembly != null)
{ Console.WriteLine("加载成功"); }
执行程序,会抛出异常,说找不到该程序集。什么原因呢?因为我们使用的是弱命名程序集,Load方法不会去全局程序集缓存中查找,而该应用程序目录下又没有该程序集,所以程序找不到。这个时候,我们把程序稍微改一下,不用添加代码,只需添加对MyAssembly的引用,重新运行程序,加载成功了。
接下来,我们就要看看Load怎么加载强命名程序集了,这个步骤稍微有些复杂。还是刚才的项目,找到MyAssembly.dll程序集所在的目录,一般在bin"Debug目录下
(1)生成密钥对文件 sn –k MyAssemblyKey.keys
你也可以自己随便起一个密钥对文件名
(2)生成公钥文件
sn –p MyAssemblyKey.keys MyAssemblyPublicKey.PublicKey
注:查看公钥命令:sn –tp MyAssemblyPublicKey.PublicKey
(3)创建强命名程序集。
很简单,只需要在声明命名空间的那句代码上加上如下特性:
[assembly:AssemblyKeyFileAttribute(@”D:"Test"MyAssemblyKey.keys”)]
(4) 编译项目
(5) 将程序集添加到程序集全局缓存
gacutil –i MyAssembly.dll
这个时候,转到加载程序集的项目中,将Load方法中的参数改为”程序集名,Version=版本,culture=区域信息,PublicKeyToken=公钥“,然后再去掉对程序集的引用,我们会发现,程序运行成功。表明Load到全局缓存区查找到了该程序集。
使用Load方法加载程序集,特别是强命名程序集,能在程序集上应用安全和部署策略,推荐使用该方法动态加载程序集,至于LoadFrom和LoadWithPartialName。
首先我们还是来看看LoadFrom方法,这个方法的原理是这样的:我们如果要使用它来动态加载程序集,必须告诉它程序集的路径,也即在哪个目录下面,CLR会去加载与你指定的路径完全匹配的程序集。记住,当我们指定程序集路径时,不能包括任何关于程序集强命名的信息,所以,CLR不会在我们指定的程序集文件上应用任何策略,而且也不会去任何其他的地方搜索程序集,简言之,它就是指哪打哪,呵呵。
例如:你有个程序集在D:/Test/MyAssembly.dll,你要用Assembly.LoadFrom加载该程序集,代码就如下:
Assembly assembly = Assembly.LoadFrom(@”D:/Test/MyAssembly.dll”);
对于,LoadWithParitalName方法,推荐大家最好不要使用它,因为程序无法确定最终要去加载哪个程序集的版本,所以我们这里只是简单的介绍一下它的工作原理:你可以传递一个程序集标识给它,包括程序集名称,至于其他信息是可选的(区域信息,公有密钥等),该方法执行时,会首先检查应用程序中配置文件的qualifyAssembly节点,如果存在,则把该部分名称的程序集替换成完全的程序集标识,如果不存在,则使用程序集名称先到应用程序根目录下查找,然后是私有目录,没有找到的话,就到程序集全局缓存中查找。简单过程如下:
应用程序根目录 -> 应用程序私有目录 -> 程序集全局缓存.
Assembly.Load()方法,Assembly.LoadFrom()方法,Assembly.LoadFile()方法的区别!
1,Assembly.Load()
这个方法通过程序集的长名称(包括程序集名,版本信息,语言文化,公钥标记)来加载程序集的,会加载此程序集引用的其他程序集,一般情况下都应该优先使用 这个方法,他的执行效率比LoadFrom要高很多,而且不会造成重复加载的问题(原因在第2点上说明)
使用这个方法的时候, CLR会应用一定的策略来查找程序集,实际上CLR按如下的顺序来定位程序集:
⑴如果程序集有强名称,在首先在全局程序集缓(GAC)中查找程序集。
⑵如果程序集的强名称没有正确指定或GAC中找不到,那么通过配置文件中的元素指定的URL来查找
⑶如果没有指定强名称或是在GAC中找不到,CLR会探测特定的文件夹:
假设你的应用程序目录是C:\AppDir,元素中的privatePath指定了一个路径Path1,你要定位的程序集是AssemblyName.dll则CLR将按照如下顺序定位程序集
C:\AppDir\AssemblyName.dll
C:\AppDir\AssemblyName\AssemblyName.dll
C:\AppDir\Path1\AssemblyName.dll
C:\AppDir\Path1\AssemblyName\AssemblyName.dll
如果以上方法不能找到程序集,会发生编译错误,如果是动态加载程序集,会在运行时抛出异常!
2,Assembly.LoadFrom()
这个方法从指定的路径来加载程序集,实际上这个方法被调用的时候,CLR会打开这个文件,获取其中的程序集版本,语言文化,公钥标记等信息,把他们传递给 Load方法,接着,Load方法采用上面的策略来查找程序集。如果找到了程序集,会和LoadFrom方法中指定的路径做比较,如果路径相同,该程序集会被认为是应用程序的一部分,如果路径不同或Load方法没有找到程序集,那该程序集只是被作为一个“数据文件”来加载,不会被认为是应用程序的一部分。 这就是在第1点中提到的Load方法比LoadFrom方法的执行效率高的原因。另外,由于可能把程序集作为“数据文件”来加载,所以使用 LoadFrom从不同路径加载相同程序集的时候会导致重复加载。当然这个方法会加载此程序集引用的其他程序集。
3,Assembly.LoadFile()
这个方法是从指定的文件来加载程序集,和上面方法的不同之处是这个方法不会加载此程序集引用的其他程序集!
结论:一般大家应该优先选择Load方法来加载程序集,如果遇到需要使用LoadFrom方法的时候,最好改变设计而用Load方法来代替!
另:Assembly.LoadFile 与 Assembly.LoadFrom的区别
1、Assembly.LoadFile只载入相应的dll文件,比如Assembly.LoadFile("abc.dll"),则载入abc.dll,假如abc.dll中引用了def.dll的话,def.dll并不会被载入。
Assembly.LoadFrom则不一样,它会载入dll文件及其引用的其他dll,比如上面的例子,def.dll也会被载入。
2、用Assembly.LoadFrom载入一个Assembly时,会先检查前面是否已经载入过相同名字的Assembly,比如abc.dll有两个版本(版本1在目录1下,版本2放在目录2下),程序一开始时载入了版本1,当使用Assembly.LoadFrom("2\\abc.dll")载入版本2时,不能载入,而是返回版本1。Assembly.LoadFile的话则不会做这样的检查,比如上面的例子换成Assembly.LoadFile的话,则能正确载入版本2。
LoadFile:加载指定路径上的程序集文件的内容。LoadFrom: 根据程序集的文件名加载程序集文件的内容。
区别:
LoadFile 方法用来来加载和检查具有相同标识但位于不同路径中的程序集.但不会加载程序的依赖项。
LoadFrom 不能用于加载标识相同但路径不同的程序集。
GetMembers、GetFields和GetMethod
原文:http://www.cnblogs.com/yaozhenfa/p/CSharp_Reflection_1.html
前期准备
在VS2012中新建一个控制台应用程序(我的命名是ReflectionStudy),这个项目是基于.net 4.0。接着我们打开Program.cs文件,按照如下在Program中写一个我们自己的类:
1 public class RefClass2 {3 private int _test3;4 private int _test1 { get; set; }5 protected int Test2 { get; set; }6 public int Test3 { get; set; }7 8 public void Show()9 {
10
11 }
12 }
窥视内部
常言道知彼知己百战不殆,所以我们第一步也是关键的一步就是要窥视RefClass类的结构(这里我们假设对RefClass并不理解)。
首先我们先要纵览全局才能继续深入,所以我们先在Main中写入如下代码:
1 static void Main(string[] args)2 {3 Type t = typeof(RefClass);4 MemberInfo[] minfos = t.GetMembers();5 foreach (MemberInfo minfo in minfos)6 {7 Console.WriteLine(minfo.Name);8 }9 Console.ReadKey();
10 }
在这里我们获取这个类的类型,然后获取了其中的公共成员(可能很多人都会认为GetMembers是获取全部,但其实只是获取公开的所有成员。)然后我们通过foreach将所有的成员的名称循环输出。
然后我们可以查看控制台的输出:

在这里我们可以看到其中不仅仅输出了我们所写类中的成员,同时还输出了父类的成员(如果不理解的这里帮你们补充下基础,Object是所有类的基类。),细心的读者一定会发现这里的输出并没有包含private和protected访问权限的成员。这就应了上面的那句话:GetMembers默认返回公开的成员。
仅仅只能看到这些公开的成员对我们来说意义并不大,所以我们需要查看到那些非公有的成员。
下面我们将上面的代码改成如下所示:
1 static void Main(string[] args)2 {3 Type t = typeof(RefClass);4 MemberInfo[] minfos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public );5 foreach (MemberInfo minfo in minfos)6 {7 Console.WriteLine(minfo.Name);8 }9 Console.ReadKey();
10 }
从中我们看到我们使用了GetMembers的重载版本,并且传入了枚举类型,分别是“包含非公开”、“包含实例成员”和“包含公开”。然后我们就可以获取到所有成员了。
最终我们将会得出下面这些成员:

到这里你可能会认为我们已经检索结束了,但是你有没有发现属性很多,而且还包含了大量的父类中的属性,假设我们只关注该类中的成员,并不关注父类中的成员该如何做呢?
其实我们只需要加上一个枚举类型(BindingFlags.DeclaredOnly):
1 MemberInfo[] minfos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly );
然后我们再查看结果:

此时就只包含该类中的成员了。
下面我们在RefClass类中添加两个静态方法,如下所示:
1 public class RefClass2 {3 private int _test3;4 private int _test1 { get; set; }5 protected int Test2 { get; set; }6 public int Test3 { get; set; }7 8 private static void Show2()9 {
10 }
11
12 public static void Show3()
13 {
14 }
15
16 public void Show()
17 {
18
19 }
20 }
然后我们继续查看,可以发现最终的结果并没有输出这些静态成员。这个时候我们只需要在GetMembers中加上一个枚举:BindingFlags.Static即可。
这里我们仅仅输出了所有的成员,但是却没有区分出是方法还是属性所以我们在Main中添加一个方法:
1 static void Main(string[] args)2 {3 Type t = typeof(RefClass);4 Func getType = (x) =>5 {6 switch (x)7 {8 case MemberTypes.Field:9 {
10 return "字段";
11 }
12 case MemberTypes.Method:
13 {
14 return "方法";
15 }
16 case MemberTypes.Property:
17 {
18 return "属性";
19 }
20 default:
21 {
22 return "未知";
23 }
24 }
25 };
26 MemberInfo[] minfos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static );
27 foreach (MemberInfo minfo in minfos)
28 {
29 Console.WriteLine(minfo.Name + ";类型:" + getType(minfo.MemberType));
30 }
31 Console.ReadKey();
32 }
这里我用了一个局部方法来根据类型输出对应的文本,因为篇幅的原因我就只判断了几个基本的类型。
最终输出的结果如下:

到此为止我们已经能够窥视整个结构。
深入窥视字段
通过上面的内容我们仅仅纵览了全局,下面我们将要继续深入,首先我们先拿字段下手。
这里我们不在使用GetMembers而需要使用GetFields(当然跟GetMembers一样如果不传入指定的枚举只返回公开的字段),代码如下所示:
1 static void Main(string[] args)2 {3 Type t = typeof(RefClass);4 FieldInfo[] finfos = t.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);5 foreach (FieldInfo finfo in finfos)6 {7 Console.WriteLine("字段名称:{0} 字段类型:{1} ", finfo.Name, finfo.FieldType.ToString());8 }9 Console.ReadKey();
10 }
最终的输出结果如下所示:

一直到这里大家都会认为我们仅仅只是分析,感觉没有什么实质的东西,下面就来点实质的东西,你可以看到_test3、_test1和Test2是私有和保护类型,
是不可以获取到它们的值的,但是我们通过反射却可以,具体的代码如下所示:
1 static void Main(string[] args)2 {3 Type t = typeof(RefClass);4 RefClass rc = new RefClass();5 rc.Test3 = 3;6 FieldInfo[] finfos = t.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);7 foreach (FieldInfo finfo in finfos)8 {9 Console.WriteLine("字段名称:{0} 字段类型:{1} rc中的值为:{2}", finfo.Name, finfo.FieldType.ToString(), finfo.GetValue(rc));
10 }
11 Console.ReadKey();
12 }
可以看到我实例化了这个类,并且设置了Test3为3,下面我通过finfo.GetValue输出了这个值,结果如下图:

现在是不是感觉有点酷了?这还没完呢,我们光获取不算什么,下面我们还要修改它的值:
1 static void Main(string[] args)2 {3 Type t = typeof(RefClass);4 RefClass rc = new RefClass();5 rc.Test3 = 3;6 FieldInfo[] finfos = t.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);7 foreach (FieldInfo finfo in finfos)8 {9 finfo.SetValue(rc, 100);
10 Console.WriteLine("字段名称:{0} 字段类型:{1} rc中的值为:{2}", finfo.Name, finfo.FieldType.ToString(), finfo.GetValue(rc));
11 }
12 Console.ReadKey();
13 }

这里我只是在foreach中增加了一条语句finfo.SetValue(rc,100),下面我们继续看最终输出的结果:

是不是现在感觉可以为所欲为了?但是还没有完。
深入窥视属性
因为属性存在get和set,并且两者都是方法,所以比较棘手。我们需要通过属性对象获取get和set方法,在通过调用他们才达到修改这个属性的值。
比如下面的代码:
1 static void Main(string[] args)2 {3 Type t = typeof(RefClass);4 RefClass rc = new RefClass();5 rc.Test3 = 3;6 PropertyInfo[] finfos = t.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);7 foreach (PropertyInfo finfo in finfos)8 {9 MethodInfo getinfo = finfo.GetGetMethod(true);
10 Console.WriteLine("get方法的名称{0} 返回值类型:{1} 参数数量:{2} MSIL代码长度:{3} 局部变量数量:{4}", getinfo.Name, getinfo.ReturnType.ToString(),
11 getinfo.GetParameters().Count(),
12 getinfo.GetMethodBody().GetILAsByteArray().Length,
13 getinfo.GetMethodBody().LocalVariables.Count);
14
15 MethodInfo setinfo = finfo.GetSetMethod(true);
16 Console.WriteLine("get方法的名称{0} 返回值类型:{1} 参数数量:{2} MSIL代码长度:{3} 局部变量数量:{4}", setinfo.Name, setinfo.ReturnType.ToString(),
17 setinfo.GetParameters().Count(),
18 setinfo.GetMethodBody().GetILAsByteArray().Length,
19 setinfo.GetMethodBody().LocalVariables.Count);
20
21 setinfo.Invoke(rc, new object[] { 123 });
22 object obj = getinfo.Invoke(rc, null);
23 Console.WriteLine("方法名:{0} 内部值:{1}", finfo.Name, obj);
24 }
25 Console.ReadKey();
26 }
这里我们循环每个属性,通过GetGetMethod获取get方法(调用该方法时如果传入true则无法获取非公开的get方法set也是一样),接着我们输出了该方法的返回类型和参数数量和MSIL代码长度以及局部变量的数量,
当然你如果有兴趣可以继续分析输入参数以及局部变量等,这里由于篇幅的缘故就不能介绍太多了。最后我们调用了set方法将值改变,然后再通过调用get方法获取这个属性的值。
最终的结果如下所示:

深入窥视方法
首先我们需要将RefClass修改成如下所示:
1 public class RefClass2 {3 private int _test3;4 private int _test1 { get; set; }5 protected int Test2 { get; set; }6 public int Test3 { get; set; }7 8 private static void Show2()9 {
10
11 }
12
13 public static string Show3(string s)
14 {
15 int b;
16 int c;
17 return s;
18 }
19
20 public string Show(string s)
21 {
22 string a;
23 return s;
24 }
25 }
主要是在方法中增加局部变量并且加上返回值,避免最后输出的时候没有值。其实这里的方法跟属性部分类似,但是为了能够完整的描述所有,所以笔者依然会讲解一遍。
下面我们直接上代码:
1 static void Main(string[] args)2 {3 Type t = typeof(RefClass);4 RefClass rc = new RefClass();5 rc.Test3 = 3;6 MethodInfo[] finfos = t.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Static );7 foreach (MethodInfo finfo in finfos)8 {9 if (finfo.GetParameters().Count() > 0 && finfo.GetParameters()[0].ParameterType == typeof(string) )
10 {
11 object obj = finfo.Invoke(rc, new[] { "123" });
12 MethodBody mbody = finfo.GetMethodBody();
13 Console.WriteLine("拥有参数的方法名:{0} 返回值类型:{1} 参数1类型:{2} 参数1名称:{3} 方法调用后返回的值:{4}",
14 finfo.Name,
15 finfo.ReturnType.ToString(),
16 finfo.GetParameters()[0].ParameterType.ToString(),
17 finfo.GetParameters()[0].Name,
18 obj.ToString());
19 }
20 else
21 {
22 MethodBody mbody = finfo.GetMethodBody();
23 Console.WriteLine("没有参数的方法名:{0} 返回值类型:{1}",
24 finfo.Name,
25 finfo.ReturnType.ToString());
26 }
27 }
28 Console.ReadKey();
29 }
在这里我进行了一些简单的判断比如判断输入参数的数量以及类型,如果不进行这些判断就会导致程序无法继续执行,具体为什么可以看下的输出结果,你就能明白笔者为什么要这么做了。
下面就是具体的结果:

读者一定发现了这其中还有get和set,你可能会认为它们不是属性吗?怎么跑到方法这里来了,其实上面我已经说了。这些其实也是方法。这也是为什么上面我需要去判断输入参数的数量以及类型的缘故。
GetCustomAttributes()
原文: https://blog.csdn.net/sujing910206/article/details/7227529
使用反射访问属性(C# 编程指南)
如果没有检索自定义属性的信息和对其进行操作的方法,则定义自定义属性并将其放置在源代码中就没有意义。C# 具有一个反射系统,可用来检索用自定义属性定义的信息。主要方法是 GetCustomAttributes,它返回对象数组,这些对象在运行时等效于源代码属性。此方法具有多个重载版本。有关更多信息,请参见 Attribute。
属性规范,如:
C#
[Author("H. Ackerman", version = 1.1)]
class SampleClass
在概念上等效于:
C#
Author anonymousAuthorObject = new Author("H. Ackerman");
anonymousAuthorObject.version = 1.1;
但是,直到查询 SampleClass 以获取属性时才会执行此代码。对 SampleClass 调用 GetCustomAttributes 会导致按上述方式构造并初始化一个 Author 对象。如果类还有其他属性,则其他属性对象的以类似方式构造。然后 GetCustomAttributes 返回 Author 对象和数组中的任何其他属性对象。之后就可以对此数组进行迭代,确定根据每个数组元素的类型所应用的属性,并从属性对象中提取信息。
示例下面是一个完整的示例。定义一个自定义属性,将其应用于若干实体并通过反射进行检索。
C#
[System.AttributeUsage(System.AttributeTargets.Class |
System.AttributeTargets.Struct,
AllowMultiple = true) // multiuse attribute
]
public class Author : System.Attribute
{
string name;
public double version;
public Author(string name)
{
this.name = name;
version = 1.0; // Default value
}
public string GetName()
{
return name;
}
}
[Author("H. Ackerman")]
private class FirstClass
{
// ...
}
// No Author attribute
private class SecondClass
{
// ...
}
[Author("H. Ackerman"), Author("M. Knott", version = 2.0)]
private class ThirdClass
{
// ...
}
class TestAuthorAttribute
{
static void Main()
{
PrintAuthorInfo(typeof(FirstClass));
PrintAuthorInfo(typeof(SecondClass));
PrintAuthorInfo(typeof(ThirdClass));
}
private static void PrintAuthorInfo(System.Type t)
{
System.Console.WriteLine("Author information for {0}", t);
System.Attribute[] attrs = System.Attribute.GetCustomAttributes(t); // reflection
foreach (System.Attribute attr in attrs)
{
if (attr is Author)
{
Author a = (Author)attr;
System.Console.WriteLine(" {0}, version {1:f}", a.GetName(), a.version);
}
}
}
}
输出Author information for FirstClass
H. Ackerman, version 1.00
Author information for SecondClass
Author information for ThirdClass
H. Ackerman, version 1.00
M. Knott, version 2.00
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
