目录node
【微信平台,此文仅受权《NCC 开源社区》订阅号发布】c#
前面三篇中,介绍了反射的基本内容和信息对象,反射主要做用于构造函数、属性、字段、方法、事件等类型成员对象;第四篇介绍了类型的实例化和事件操做。数组
本篇介绍类型的成员操做和实践练习。缓存
因为内容较多,多动手实践一下。微信
那么,如何经过 Type 获取相应的成员呢?ide
以上方法具备获取单个成员或多个成员的版本。函数
全部的 *Info 实例都会在第一次使用时,由反射 API 缓存起来,这种缓存有助于优化 API 的性能。性能
MemberInfo 能够获取有关成员属性的信息,并提供对成员元数据的访问权限。测试
MemberInfo 类是用于获取有关类的全部成员(构造函数、事件、字段、方法和属性)的信息的类的抽象基类。优化
由图片1能够看到,MemberInfo 是全部反射类型的基类,此类为全部成员提供了基本功能。
使用 GetMember()
或 GetMembers()
能够获取类型的一个或多个成员。
GetMembers()
该方法会返回当前类型(及其基类)的全部公有成员。
GetMember
方法能够经过名称检索特定的成员。因为成员(方法、属性等)可能会被重载,所以该方法会返回一个数组。
例如
MemberInfo[] members = type.GetMember("test");
建立一个类型
public class MyClass { private static string A { get; set; } public string B; public string C { get; set; } [Required] public int Id { get; set; } [Phone] public string Phone { get; set; } [EmailAddress] public string Email { get; set; } static MyClass() { A = "666"; } public MyClass() { B = "666"; } public MyClass(string message) { C = message; } public string Add(string a, string b) { return a + b; } }
打印
Type type = typeof(MyClass); MemberInfo[] members = type.GetMembers(); foreach (var item in members) { Console.WriteLine(item.Name + " | " + item.MemberType); }
输出
get_C | Method set_C | Method get_Id | Method set_Id | Method get_Phone | Method set_Phone | Method get_Email | Method set_Email | Method Add | Method GetType | Method ToString | Method Equals | Method GetHashCode | Method .ctor | Constructor .ctor | Constructor C | Property Id | Property Phone | Property Email | Property B | Field
MemberInfo
中有个 MemberType 枚举的属性 名为 MemberType 。
MemberType
枚举的定义以下
名称 | 值 | 说明 |
---|---|---|
All | 191 | 指定全部成员类型 |
Constructor | 1 | 指定该成员是构造函数 |
Custom | 64 | 指定该成员是自定义成员类型 |
Event | 2 | 指定该成员是事件 |
Field | 4 | 指定该成员是字段 |
Method | 8 | 指定该成员是方法 |
NestedType | 128 | 指定该成员是嵌套类型 |
Property | 16 | 指定该成员是属性 |
TypeInfo | 32 | 指定该成员是类型 |
其中 MemverType.All
的定义以下 All = NestedType | TypeInfo | Property | Method | Field | Event | Constructor
。
下面的例子是经过 GetMembers 获取到 方法成员,而且传递参数调用。
这里只是示例一下,关于方法的实例化和调用,在本文的第三节。
MemberInfo[] members = type.GetMembers(); foreach (var item in members) { // 若是成员属于方法 if (item.MemberType == MemberTypes.Method) { // 输出此方法的参数列表:参数类型+参数名称 foreach (ParameterInfo pi in ((MethodInfo)item).GetParameters()) { Console.WriteLine("Parameter: Type={0}, Name={1}", pi.ParameterType, pi.Name); } // 若是是方法有两个参数,则调用 if (((MethodInfo)item).GetParameters().Length == 2) { // 调用一个方法以及传递参数 MethodInfo method = (MethodInfo)item; Console.WriteLine("调用一个方法,输出结果:"); Console.WriteLine(method.Invoke(example, new object[] { "1", "2" })); } } }
MemberInfo 中,有三种获取类型的属性:
由于一个方法能够继承,也能够重写,那么不少时候判断和调用,就须要了解相关信息;
DeclaringType :一个类型中使用了父类或者本身的方法,那么返回此方法的出处;
ReflectedType :从哪一个类型中获取,就返回哪一个类型;即从个 Type 里得到成员实例,就返回这个 Type 的名称;
新建一个两个类型
/// <summary> /// 父类 /// </summary> public class MyClassFather { /// <summary> /// 重写 ToString() /// </summary> /// <returns></returns> public override string ToString() { return base.ToString(); } } /// <summary> /// 子类 /// </summary> public class MyClassSon : MyClassFather { }
控制台 Program.Main 中,编写
Type typeFather = typeof(MyClassFather); Type typeSon = typeof(MyClassSon); Type typeObj = typeof(object); Type typeProgram = typeof(Program); // 为了省步骤,就不用 MemberInfo 了 MethodInfo methodObj = typeObj.GetMethod("ToString"); MethodInfo methodFather = typeFather.GetMethod("ToString"); MethodInfo methodSon = typeSon.GetMethod("ToString"); MethodInfo methodProgram = typeProgram.GetMethod("ToString");
打印 DeclaringType
Console.WriteLine(methodObj.DeclaringType); Console.WriteLine(methodFather.DeclaringType); Console.WriteLine(methodSon.DeclaringType); Console.WriteLine(methodProgram.DeclaringType);
输出
System.Object Mytest.MyClassFather Mytest.MyClassFather System.Object
解析:
MyClassFather 对 ToString
方法进行了重写,因此 DeclaringType
获取到的类型就是 MyClassFather ;
MyClassSon 继承了 MyClassFather,直接使用父类的 ToString()
方法,因此返回的是 MyClassFather ;
Program 没有对 ToString()
进行重写,因此返回的是 Object;
笔者的 IL 知识很是薄弱,只能列出一些简单的内容。
在最前面的练习中,咱们发现
public string C { get; set; }
输出了
get_C | Method set_C | Method C | Property
生成的 IL 是这样的
.property instance string C() { .get instance string Mytest.MyClass::get_C() .set instance void Mytest.MyClass::set_C(string) }
属性、索引器、事件生成的 IL 总结:
上面三种类型,生成 IL 时,都会有相应的 方法生成,经过 GetMethods()
或者 GetMembers()
能够获取到。
定义一个类型
public class MyClass { private string Test; public string A { get { return Test; } } public string B { set { Test = value; } } public string C { get; set; } }
从前面的实例中,有很多是获取属性列表的示例,可是没法从中识别出里面的构造,例如上面的 MyClass 类型。
PropertyInfo
中有个 GetAccessors()
方法,能够获取相应的信息。
方法 | 使用说明 |
---|---|
GetAccessors() | 返回一个数组,其元素反射了由当前实例反射的属性的公共 get 和 set 访问器。 |
GetAccessors(Boolean) | 返回一个数组,其元素反射了当前实例反射的属性的公共及非公共(若是指定)get 和 set 取值函数。 |
使用示例
Type type = typeof(MyClass); PropertyInfo[] list = type.GetProperties(); foreach (var item in list) { var c = item.GetAccessors(); foreach (var node in c) { Console.WriteLine(node); } Console.WriteLine("************"); }
输出
System.String get_A() ************ Void set_B(System.String) ************ System.String get_C() Void set_C(System.String) ************
若是将上面的属性 C 改为
public string C { get; private set; }
那么只能输出
System.String get_A() ************ Void set_B(System.String) ************ System.String get_C() ************
若是想获取私有构造器,可使用.GetAccessors(true)
。
从反射和 IL 咱们得知,一个属性会自动生成两个方法。
那么咱们经过 PropertyInfo 能够获取到这些方法。
方法 | 使用说明 |
---|---|
GetSetMethod | 获取 set 方法,返回 MethodInfo |
GetGetMethod | 获取 get 方法,返回 MethodInfo |
GetAccessors | 获取上面两个方法的集合,返回 MethodInfo[] |
建立一个属性
public string C { get; set; }
Type type = typeof(MyClass); PropertyInfo property = type.GetProperty("C"); // 指定获取 get 或 set MethodInfo set = property.GetSetMethod(); MethodInfo get = property.GetGetMethod(); MethodInfo[] all = property.GetAccessors();
咱们要记得,反射,是对元数据的利用;只有实例才能被执行调用。
在这里,说一下 nameof
关键字,nameof 没有任何做用,他不会对程序产生任何影响。
nameof(T) 能够输出 T,例如 namaof(Program)
输出 Program
。
那么什么状况下使用到他呢?
咱们在写代码时,会使用到例如 Visual Studio 等 IDE,若是使用 nameof,里面的类型是强类型的,能够查找引用、跳转、获取注释等。若是须要重构,也能够快速重命名全部引用。
若是直接使用字符串的话,容易拼错命名、一旦修改一个命名,须要手动找到全部字符串进行修改。
调用一个实例方法有以下步骤:
步骤 | 类型 | 说明 |
---|---|---|
获取 Type | Type | 经过程序集等各类方式获取 Type 类型 |
获取实例 | object | 经过 Activator.CreateInstance(type); 建立实例 |
获取方法 | MethodInfo或 MemberInfo | 经过 Type 获取对应的方法 |
设置参数列表 | object[] parameters | 调用方法时传递的参数 |
执行方法 | .Invoke() 方法 | 执行 MethodInfo.Invoke() |
获取返回结果 | object | 执行方法获取到返回结果 |
首先咱们定义一个类型
public class MyClass { /// <summary> /// 无参数,五返回值 /// </summary> public void A() { Console.WriteLine("A被执行"); } /// <summary> /// 有参数,有返回值 /// </summary> /// <param name="a"></param> /// <returns></returns> public string B(string left) { Console.WriteLine("执行 B(string left)"); return left + "666"; } public string C(string left,int right) { Console.WriteLine("执行 C(string left,int right)"); return left + right; } public string C(string left, string right) { Console.WriteLine("执行 C(string left, string right)"); return left + right; } }
在 Program 编写代码获取到类型的 Type 以及建立实例。
Type type = typeof(MyClass); object example = Activator.CreateInstance(type);
咱们来调用方法 A()
// 获取 A MethodInfo methodA = type.GetMethod(nameof(MyClass.A)); // 传递实例,而且执行实例的 A 方法 methodA.Invoke(example, new Object[] { });
方法 B
有一个参数,咱们调用时添加参数进去
object result; // 获取 B MethodInfo methodB = type.GetMethod(nameof(MyClass.B)); // 传递参数 // 执行获取返回结果 result = methodB.Invoke(example, new[] {"测试"});
前面 1.1 中,示例有关于获取方法参数的代码。这里再也不赘述
在 《C# 反射与特性》系列的第四篇,咱们介绍了构造函数 ConstructorInfo 的调用和重载,MethodInfo 实际上也是差很少的。
上面咱们使用了 type.GetMethod("方法名称")
的方法获取了 MethodInfo ,对于 MyClass.C
,有两个重载,那么咱们能够这样指定要使用的重载方法
// 获取 C // 执行获取返回结果 MethodInfo methodC = type.GetMethod(nameof(MyClass.C), new Type[] {typeof(string), typeof(string)}); result = methodC.Invoke(example, new string[] {"测试", "测试"}); // result = methodC.Invoke(example, new Object[] {"测试", "测试"});
至此,对于类型、构造函数、委托、方法的实例化与操做,已经讲了一次。
下面将说一下属性和字段如何设置值和获取值。