C# 反射机制(转载)
一、反射定义:
审查元数据并收集关于它的类型信息的能力。元数据(编译以后的最基本数据单元)就是一大堆的表,当编译程序集或者模块时,编译器会创建一个类定义表,一个字段定义表,和一个方法定义表等。通过反射,可以在运行时获得.NET中每一个类型(包括类、结构、委托、接口和枚举等)的成员,包括方法、属性、事件,以及构造函数等,还可以获得每个成员的名称、限定符和参数等。
应用程序结构分为应用程序域(AppDomain)—程序集(Assembly)—模块(Module)—类型(class)—成员几个层次,公共语言运行库(CLR)加载器管理应用程序域,这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局。程序集包含模块,而模块包含类型,类型又包含成员,反射则提供了封装程序集、模块和类型的对象。我们可以使用反射动态地创建类型的实例,将类型绑定到现有对象或从现有对象中获取类型,然后调用类型的方法或访问其字段和属性。
二、反射的三大功能:
1、查看和遍历类型(及其成员)的基本信息和程序集元数据(metadata) --- 本篇讲述。
2、迟绑定(Late-Binding)方法和属性。
3、动态创建类型实例(并可以动态调用所创建的实例的方法、字段、属性)。
三、Type类型
反射的核心是Type类,这个类封装了关于对象的信息,也是进行反射的入口。我们可以通过Type对象获得类型的方法、字段、属性、事件、参数、构造函数以及在其中部署该类的模块和程序集。
获取Type对象,有多种方法,下面列举3个从当前加载程序集中的获取Type对象(Runtime)的方法:
通过Type的静态方法GetType()
Type MyType = Type.GetType("System.IO.BinaryReader");
注意:传入的参数是个字符串。
使用typeof操作符
Type MyType = typeof(System.IO.BinaryReader);
注意:传入的是个类型参数
通过类型实例获得Type对象
string str = "aaa";
Type MyType = str.GetType();
注意:获取的只是System.String类型的Type对象,不包含关于这个特定对象值信息。
四、反射程序集(exe、dll)
上面讲的获取Type对象的方法只能从当前程序集中获取,如果我们要获取外部程序集的Type对象呢? System.Reflection命名空间下的Assembly类型提供了获取程序集的多种方法,下面仅列举2种:
使用Assembly的静态方法Load()
Assembly asm = Assembly.Load("user"); //必须先引用user.dll
注意:Load()方法只需给出程序集的名称,而不需要提供程序集的后缀名,系统会先后在全局程序集缓冲区、应用程序基目录和私有路径下面查找该程序集,如果找不到该程序集系统抛出异常。
通过Assembly的静态方法LoadFrom()
Assembly asm = Assembly.LoadFrom("user.dll"); //必须先引用user.dll
注意:LoadFrom()方法需要提供程序集的文件名(包括后缀),如果想加载一个不属于当前项目的程序集,则需要给出全路径:
Assembly asm = Assembly.LoadFrom(@"E:\Windows\System32\user.dll");
通过Assembly的静态方法获取当前程序集
Assembly as = Assembly.GetExecutingAssembly(); //等同于 Assembly as = Assembly.LoadFrom("项目名.exe");
Assembly的GetType()方法可以获取该程序集的Type对象集合
Type [] types = Assembly.GetType();
Assembly常用的属性和方法:
FullName 程序集名称
Location 程序集的路径
GetTypes() 获取程序集包含的全部类型
GetType() 获取某个类型
GetModules() 获取程序集包含的模块
GetModule() 获取某个模块
GetCustomAttributes() 获取自定义特性信息
备注:一个程序集包含多个模块(Module),每个模块又包含多个类型(Type),一个程序集包含多个命名空间,但同一个命名空间可能放在多个程序集中。
Assembly asm = Assembly.Load("test.exe"); //当前程序名为test.exe
Console.WriteLine("程序集名称:{0}", asm.FullName);
Console.WriteLine("程序集的路径:{0}",asm.Location);
五、与反射有关的类
(1)使用System.Reflection.Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。可以使用Type的Assembly属性获取或使用Assembly的load方法获取。
(2)使用System.Reflection.Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
(3)使用System.Reflection.ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetConstructors或 GetConstructor方法来调用特定的构造函数。
(4)使用System.Reflection.MemberInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法来调用特定的方法。
(5)使用System.Reflection.FieldInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。使用Type的GetField 或 GetFields方法来调用特定的构造函数。
(6)使用System.Reflection.EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。使用Type的GetEvent或 GetEvents方法来调用特定的构造函数。
(7)使用System.Reflection.PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。使用Type的GetProperty或 GetPropertys方法来调用特定的构造函数。
(8)使用System.Reflection.ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。使用MethodInfo的GetParameters
方法来调用特定的构造函数。
(9)使用System.Reflection.MethodInfo了解参数的名称、数据类型、返回值类型,返回的参数等。使用Type的GetMethod或GetMethods方法来调用特定的构造函数。
六、反射应用举例
我们先构建一个简单User类作为例子:
class User
{
private string name;
private readonly int age;
public string address;
public const string text = "Const Field";
public string Name
{
private get { return name; }
set { name = value; }
}
}
1、反射类型的基本信息
通过Type对象可以获取类型的基本信息,下面是Type类的一些常用的属性:
Name 只返回成员的简单名称,不返回完全限定名(可通过Type的MethodInfo.Name获取
FullName 类型全名
Namespace 命名空间名称
BaseType 获取对于基类的Type类型的引用
UnderlyingSystemType 在.Net中映射的类型的引用
Attributes 获取TypeAttributes位标记
IsValueType 是否值类型
IsByRef 是否由引用传递
IsEnum 是否枚举
IsClass 是否类
IsInterface 是否接口
IsSealed 是否密封类
IsPrimitive 是否基类型(比如int)
IsAbstract 是否抽象
IsPublic 是否公开
IsNotPublic 是否非公开
IsVisible 是否程序集可见
举例:
Type t = typeof(User);
Console.WriteLine("命名空间:{0}", t.Namespace);
Console.WriteLine("类型名称:{0}", t.Name);
Console.WriteLine("类型全名:{0}", t.FullName);
Console.WriteLine("对于基类的Type类型的引用:{0}", t.BaseType);
Console.WriteLine("在.Net中映射的类型的引用:{0}", t.UnderlyingSystemType);
Console.WriteLine("在TypeAttributes位标记:{0}", t.Attributes);
Console.WriteLine("是否值类型:{0}", t.IsValueType);
Console.WriteLine("是否由引用传递:{0}", t.IsByRef);
Console.WriteLine("是否枚举:{0}", t.IsEnum);
Console.WriteLine("是否类:{0}", t.IsClass);
Console.WriteLine("是否接口:{0}", t.IsInterface);
Console.WriteLine("是否密封类:{0}", t.IsSealed);
Console.WriteLine("是否抽象:{0}", t.IsAbstract);
Console.WriteLine("是否基类型:{0}", t.IsPrimitive);
Console.WriteLine("是否公开:{0}", t.IsPublic);
Console.WriteLine("是否非公开:{0}", t.IsNotPublic);
Console.WriteLine("是否程序集可见:{0}", t.IsVisible);
运行结果:
命名空间:Test
类型名称:User
类型全名:Test.User
对于基类的Type类型的引用:System.Object
在.Net中映射的类型的引用:Test.User
在TypeAttributes位标记:AutoLayout, AnsiClass, Class, BeforeFieldInit
是否值类型:False
是否由引用传递:False
是否枚举:False
是否类:True
是否接口:False
是否密封类:False
是否抽象:False
是否基类型:False
是否公开:False
是否非公开:True
是否程序集可见:False
2、MemberInfo(成员信息)类型
MemberInfo是一个基类,Type 类型以及所有的Info类型均 继承自 MemberInfo 类型,MemberInfo类型提供了获取类型基础信息的能力。
Type t = typeof(User);
MemberInfo[] memberInfo = t.GetMembers(); //可传入类型参数
Console.WriteLine("查看类型{0}的成员信息", t.Name);
foreach (MemberInfo mi in memberInfo)
{
Console.WriteLine("成员:{0} 类型:{1}", mi.Name, mi.MemberType);
}
运行结果:
查看类型User的成员信息
成员:set_Name 类型:Method
成员:ToString 类型:Method
成员:Equals 类型:Method
成员:GetHashCode 类型:Method
成员:GetType 类型:Method
成员:.ctor 类型:Constructor
成员:Name 类型:Property
成员:address 类型:Field
成员:text 类型:Field
分析:Name属性在编译后成为了get_Name()和set_Name()两个独立的方法,基类System.Object的成员GetType()和Equals()也被打印了出来,所有private字段的属性都没有打印出来,因为默认GetMembers()方法只获取公共成员(public),我们可以使用GetMembers的重载方法,传入BindingFlags 位标记参数来选择要获取的成员类型。
MemberInfo[] memberInfo = t.GetMembers(
BindingFlags.Public | //公共成员
BindingFlags.Static | //为了获取返回值,必须指定 BindingFlags.Instance 或 BindingFlags.Static。
BindingFlags.NonPublic | //非公共成员(即私有成员和受保护的成员)
BindingFlags.Instance | //为了获取返回值,必须指定 BindingFlags.Instance 或 BindingFlags.Static。
BindingFlags.DeclaredOnly //仅搜索 Type 上声明的成员,而不搜索被简单继承的成员
);
再次运行结果:
查看类型User的成员信息
成员:get_Name 类型:Method
成员:set_Name 类型:Method
成员:ToString 类型:Method
成员:Equals 类型:Method
成员:GetHashCode 类型:Method
成员:GetType 类型:Method
成员:Finalize 类型:Method
成员:MemberwiseClone 类型:Method
成员:.ctor 类型:Constructor
成员:Name 类型:Property
成员:name 类型:Field
成员:age 类型:Field
成员:address 类型:Field
成员:text 类型:Field
分析:继承自基类 System.Object 的方法都被过滤掉了,同时,打印出了私有的 name, age等字段。
使用 Type类的FindMembers()方法可以获取指定类型的所有成员。
MemberInfo[] memberInfo = t.FindMembers(
MemberTypes.Method, // 说明查找的成员类型为 Method,可以为All、Constructor、Field、Event等
BindingFlags.Public |
BindingFlags.Static |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.DeclaredOnly,
Type.FilterName, //指定按名称过滤
"*" //指定为返回所有值(如为"get*"即返回以get开头的名称)
);
FindMembers运行结果:
查看类型User的成员信息
成员:get_Name 类型:Method
成员:set_Name 类型:Method
分析:只获取到了类型为Method的成员
注意:MemberInfo 类有两个属性值得注意,一个是DeclaringType,一个是 ReflectedType,返回的都是Type类型。DeclaredType 返回的是声明该成员的类,而ReflectedType返回的是获取 MemberInfo 的此实例的类对象。比如上面例子中获取到的继承自基类的Equals()方法,它的DeclaringType返回是typeof(Object)的Type,而ReflectedType返回的是typeof(User)的Type,因为Equals()是在System.Object中声明的,而却是被User调用并被捕获的。
3、FieldInfo(字段信息)类型
Type t = typeof(User);
FieldInfo[] fields = t.GetFields();
Console.WriteLine("查看类型:{0}的字段信息", t.Name);
foreach (FieldInfo fi in fields)
{
Console.WriteLine("名称:{0},类型:{1},属性:{2}", fi.Name, fi.FieldType, fi.Attributes);
}
运行结果:
查看类型:User的字段信息
名称:address,类型:System.String,属性:Public
名称:text,类型:System.String,属性:Public, Static, Literal, HasDefault
备注:FieldInfo也是继承自MemberInfo类,同样,可以在GetFields()中传入BindingFlags 位标记参数来获取私有字段信息。
4、PropertyInfo(属性)类型
Type t = typeof(User);
Console.WriteLine("查看类型:{0}的属性信息", t.Name);
PropertyInfo[] properties = t.GetProperties();
foreach (PropertyInfo pi in properties)
{
Console.WriteLine("名称:{0},类型:{1},可读:{2},可写:{3},属性:{4}", pi.Name, pi.PropertyType, pi.CanRead, pi.CanWrite, pi.Attributes);
MethodInfo setMi = pi.GetSetMethod(); //获取name的set方法
MethodInfo getMi = pi.GetGetMethod(); //获取name的get方法
}
运行结果:
查看类型:User的属性信息
名称:Name,类型:System.String,可读:True,可写:True,属性:None
备注:我们将含有get和/或set访问器的类成员称为“属性”(英文Property),PropertyInfo也是继承自MemberInfo类,同样,可以在GetProperties()中传入BindingFlags 位标记参数来获取私有属性信息。
User类中的name字段在编译后会生成get_Name()和set_Name()两个方法,我们可以通过GetSetMethod()和GetGetMethod()方法来获取
5、MethodInfo(方法信息)类型
Type t = typeof(User);
Console.WriteLine("查看类型:{0}的方法信息", t.Name);
MethodInfo[] methods = t.GetMethods();
foreach (MethodInfo method in methods)
{
Console.WriteLine("名称:{0},签名:{1},属性:{2},返回值类型:{3},{4}", method.Name, method, method.Attributes, method.ReturnType);
ParameterInfo []pi = method.GetParameters(); //获取
}
运行结果:
查看类型:User的方法信息
名称:set_Name,签名:Void set_Name(System.String),属性:PrivateScope, Public, HideBySig, SpecialName,返回值类型:System.Void
名称:ToString,签名:System.String ToString(),属性:PrivateScope, Public, Virtual,HideBySig, VtableLayoutMask,返回值类型:System.String
名称:Equals,签名:Boolean Equals(System.Object),属性:PrivateScope, Public, Virtual, HideBySig, VtableLayoutMask,返回值类型:System.Boolean
名称:GetHashCode,签名:Int32 GetHashCode(),属性:PrivateScope, Public, Virtual, HideBySig, VtableLayoutMask,返回值类型:System.Int32
名称:GetType,签名:System.Type GetType(),属性:PrivateScope, Public, HideBySig,返回值类型:System.Type
————————————————
版权声明:本文为CSDN博主「夜之子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/chen_zw/article/details/7956877
浙公网安备 33010602011771号