C# 反射进阶
前言
一、反射的定义
审查元数据并收集关于它的类型信息的能力。
二、反射的基础概念
(1)Assembly:定义和加载程序集,加载在程序集中的所有模块以及从此程序集中查找类型并创建该类型的实例。
(2)Module:获取包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
(3)ConstructorInfo:获取构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。
(4)MethodInfo(GetMethod/GetMethods):获取方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。
(5)FiedInfo(GetField/GetFields):获取字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。
(6)EventInfo(GetEvent/GetEvents):获取事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。
(7)PropertyInfo(GetProperty/GetProperties):获取属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。
(8)ParameterInfo:获取参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。
(9)MemberInfo(GetMember/GetMembers):获取字段、事件、属性等各种信息
测试代码
测试类Person中存在私有属性,共有属性,无参构造函数,有参构造函数,方法等等
public class Person { private int id; public int Id { get => id; set => id = value; } public string Name { set; get; } public string Phone { get; set; } public Person() { } public Person(string a, int b) { this.Name = a; this.Phone = b.ToString(); } public Person(int id, string name, string phone) { this.Id = id; this.Name = name; this.Phone = phone; } public string getName() { return this.Name; } public string getName1(string str) { return str; } public string getPhone() { return this.Phone; } public string getPhone(string str) { return this.Phone+str; } public string getPhone(string str,int num) { return this.Phone + str+num; } private void privateMethod(string a) { Console.WriteLine("这是一个私有方法,传入的参数是:"+a); } }
现在来利用反射创建不带参数的对象
/// <summary> /// 创建不带参数的对象 /// </summary> /// <returns></returns> private static Person CreateWithoutParms() { Assembly assembly = Assembly.Load("TestClass");//加载程序集 Type type = assembly.GetType("TestClass.Person");//获取类名称(要带上命名空间) object o = Activator.CreateInstance(type);//创建Person实体,无参构造 Person person = o as Person; return person; }
调用只需要
Person person = CreateWithoutParms(); person.Name = "张三"; Console.WriteLine("person.Name:"+ person.Name);
输出
现在来利用反射创建带参数的对象
/// <summary> /// 创建带参数的对象 /// </summary> /// <returns></returns> private static Person CreateWithParms() { Assembly assembly = Assembly.Load("TestClass");//加载程序集 Type type = assembly.GetType("TestClass.Person");//获取类名称(要带上命名空间) object o = Activator.CreateInstance(type, new object[] {"a",666 });//创建Person实体,有参构造 Person person = o as Person; return person; }
调用只需要
Person person = CreateWithParms(); Console.WriteLine("person.Name:"+person.Name+ " person.Phone:" + person.Phone);
输出
现在来调用公共方法
调用有重载和无重载方法的关键,就是在GetMethod中是否传递参数的类型。
/// <summary> /// 调用带参数的方法(无重载) /// </summary> /// <returns></returns> private static string CallFunction() { Assembly assembly= Assembly.Load("TestClass"); Type type = assembly.GetType("TestClass.Person"); object o = Activator.CreateInstance(type); MethodInfo methodInfo = type.GetMethod("getName1"); string result=methodInfo.Invoke(o, new object[] { "这是传入参数" }).ToString(); return result; }
调用
string rsult = CallFunction(); Console.WriteLine(rsult);
结果
调用私有方法
调用公共方法与私有方法的区别就是在调用type的GetMethod方法时,是否设置"BindingFlags.Instance | BindingFlags.NonPublic"
/// <summary> /// 调用私有方法 /// </summary> private static void CallPrivateFunction() { Assembly assembly = Assembly.Load("TestClass"); Type type = assembly.GetType("TestClass.Person"); object o = Activator.CreateInstance(type); MethodInfo methodInfo = type.GetMethod("privateMethod", BindingFlags.Instance | BindingFlags.NonPublic); methodInfo.Invoke(o, new object[] { "张三"}); }
结果
获取操作属性
/// <summary> /// 获取与操作属性 /// </summary> /// <param name="propertyName"></param> /// <param name="propertyValue"></param> private static void getAndSetProperity(string propertyName,string propertyValue) { //1.通过反射创建Person实体 Assembly assembly = Assembly.Load("TestClass"); Type type = assembly.GetType("TestClass.Person"); object o = Activator.CreateInstance(type, new object[] { "张三", 131000000 }); PropertyInfo propertyInfo=type.GetProperty(propertyName); Console.WriteLine("修改前Phone:"+ propertyInfo.GetValue(o));//获取属性值 propertyInfo.SetValue(o, propertyValue);//设置属性值 Console.WriteLine("修改后Phone:" + propertyInfo.GetValue(o)); }
运行此方法
获取操作字段
/// <summary> /// 获取与操作字段 /// </summary> /// <param name="fieldName"></param> /// <param name="fieldValue"></param> private static void getAndSetField(string fieldName, int fieldValue) { Assembly assembly = Assembly.Load("TestClass"); Type type = assembly.GetType("TestClass.Person"); object o = Activator.CreateInstance(type, new object[] {1, "张三", "131000000" }); FieldInfo fieldInfo = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); Console.WriteLine("修改前id"+ fieldInfo.GetValue(o)); fieldInfo.SetValue(o, fieldValue); Console.WriteLine("修改后id" + fieldInfo.GetValue(o)); }
调用结果
创建泛型类并调用
/// <summary> /// 调用泛型类中的方法 /// </summary> private static void GenericWithParms() { Assembly assembly = Assembly.Load("TestClass"); Type type = assembly.GetType("TestClass.GenericClass`3"); type= type.MakeGenericType(new Type[] { typeof(string),typeof(int),typeof(DateTime)}); object o = Activator.CreateInstance(type); MethodInfo methodInfo = type.GetMethod("PrintParm"); methodInfo = methodInfo.MakeGenericMethod(new Type[] { typeof(string), typeof(int), typeof(DateTime) }); methodInfo.Invoke(o, new object[] {"张三",666,DateTime.Now}); }
结果
针对以上代码,做出以下几点说明:
1).
只有在创建泛型类时,才需要做这样的设置,数字为泛型类总参数的个数
2).
在创建泛型实体之前,要通过type的MakeGenericType方法,设置传入的参数类型
3).
同创建泛型类一样,在调用泛型方法前,也需要设置泛型方法的参数类型
4).如果调用的是泛型类中的普通方法,无需设置泛型方法的参数类型,反之,如果调用的是普通类中的泛型方法,无需设置泛型类参数个数,也无需设置参数类型