参考资料:
C#反射机制 - 知乎 (zhihu.com)
一、基本概念
.Net的应用程序由几个部分:‘程序集(Assembly)’、‘模块(Module)’、‘类型(class)’组成,而反射提供一种编程的方式,让程序员可以在程序运行期获得这几个组成部分的相关信息。
Assembly——可以获得正在运行的装配件信息,也可以动态的加载装配件,以及在装配件中查找类型信息,并创建该类型的实例。
装配件是.Net应用程序执行的最小单位,编译出来的.dll、.exe都是装配件。
Module——了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
Type——可以获得对象的类型信息,此信息包含对象的所有要素:方法、构造器、属性等等,通过Type类可以得到这些要素的信息,并且调用之。
ConstructorInfo——了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等
MethodInfo——包含方法的信息,通过这个类可以得到方法的名称、参数、返回值等,并且可以调用之。
EventInfo——了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。
PropertyInfo——了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。
ParameterInfo——了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。
.NET中的重要机制,通过反射,可以在运行时获得程序或程序集中每一个类型(包括类、结构、委托、接口和枚举等)的成员和成员的信息。有了反射,即可对每一个类型了如指掌。另外我还可以直接创建对象,即使这个对象的类型在编译时还不知道。
重点:
System.Type类:System.Type 类对于反射起着核心的作用。但它是一个抽象的基类,Type有与每种数据类型对应的派生类,我们使用这个派生类的对象的方法、字段、属性来查找有关该类型的所有信息。获取给定类型的Type引用有3种常用方式:
//2.1、使用C# typeof运算符,是支持强类型的, 前提是项目必须引用反射类所在的程序集
Type personType1 = typeof(Person);
//2.2 使用对象GetType方法
Person p1 = new Person();
Type personType2 = p1.GetType();
//2.3调用Type类的静态方法GetType(),Type.GetType()是非强类型,如果参数typeName表示的目标类型不在当前程序集中,那么会返回nul
Type personType3 = Type.GetType("ReflectionStudy.Model.Person");
//此时会返回null,正确的做法是加载程序集,通过2.4的方式去获取
//2.4通过程序集类的Assembly的静态方法GetType()
Assembly assembly2 = Assembly.Load("ReflectionStudy.Model");
Type personType4 = assembly2.GetType("ReflectionStudy.Model.Person");
Type的属性及含义:
Type的方法:
二、实际应用
创建一个实体类库ReflectionStudy.Model,创建两个实体类
namespace ReflectionStudy.Model { //公司 internal class Company { public string Name { get; set; } public string Description { get; set; } public string Owner { get; set; } } //人员 public class Person { public string Name { get; set; } public int Age { get; set; } public int Id { get; set; } //不带set\get的为字段,带set/get的为属性 public string Introduction; //公开方法 public void IntroduceMyself(string word) { Console.WriteLine(word); } //私有方法 private void TakeShower() { Console.WriteLine("洗澡"); }
//带参数的公共方法
public void WriteString(string s, int i)
{
Console.WriteLine("WriteString:" + s + i.ToString());
}
//带一个参数的静态方法
public static void StaticWriteString(string s)
{
Console.WriteLine("StaticWriteString:" + s);
}
} }
创建一个控制台程序ReflectionStudy,引用程序集,在program.cs当中创建对应的方法
1、读取程序集
public static void GetProgramAssembly() { //读取已引用的程序集 // 引用的程序会编译后,生成dll文件存放在bin目录 //D:\repos2022\ReflectionStudy\bin\Debug\net5.0\ReflectionStudy.Model.dll //获取ReflectionStudy.Model.dll中的实体类 //三种方式等价: Assembly assembly1 = Assembly.LoadFrom("ReflectionStudy.Model.dll"); Assembly assembly2 = Assembly.Load("ReflectionStudy.Model"); Assembly assembly3 = Assembly.LoadFile(@"D:\repos2022\ReflectionStudy\bin\Debug\net5.0\ReflectionStudy.Model.dll"); //遍历程序集中的所有class foreach (var type in assembly3.GetTypes()) { Console.WriteLine($"名称:{type.Name},属性:{type.BaseType.Name}"); } }
运行结果:
//1、读取程序集
{ GetProgramAssembly(); //结果打印: //名称:Person,属性:Object //名称:Company,属性:Object }
2、根据类型创建实体类, Activator.CreateInstance(Type,。。。),第一个参数为需要创建对象的类型,后面的为调用构造函数的参数,下面例子为调用无参构造函数。
public static void GetEntityInfo() { //1、加载程序集 Assembly assembly = Assembly.Load("ReflectionStudy.Model"); //2、获取类型,注意带上完整的命名空间,三种方式 Type personType = assembly.GetType("ReflectionStudy.Model.Person"); //3、根据类型创建实体类 object? person = Activator.CreateInstance(personType); //4.1、遍历属性 foreach (var property in personType.GetProperties()) { Console.WriteLine($"类:{personType.Name},属性:{property.Name}"); } //4.2、遍历类的方法 foreach (var method in personType.GetMethods()) { Console.WriteLine($"类:{personType.Name},方法:{method.Name}"); } //4.3遍历字段 foreach (var field in personType.GetFields()) { Console.WriteLine($"类:{personType.Name},字段:{field.Name}"); } }
运行结果:
//2、利用反射获取类的相关信息(字段、属性、方法) { GetEntityInfo(); //结果打印 //类:Person,属性:Name //类:Person,属性:Age //类:Person,属性:Id //类:Person,方法:get_Name //类:Person,方法:set_Name //类:Person,方法:get_Age //类:Person,方法:set_Age //类:Person,方法:get_Id //类:Person,方法:set_Id //类:Person,方法:IntroduceMyself //类:Person,方法:GetType //类:Person,方法:ToString //类:Person,方法:Equals //类:Person,方法:GetHashCode //类:Person,字段:Introduction }
3、利用反射给类里的字段赋值、执行类里的方法
//设置、获取属性;执行方法 public static void GetOrSetProperties() { //1、加载程序集 Assembly assembly = Assembly.Load("ReflectionStudy.Model"); //2、获取类型,注意带上完整的命名空间,三种方式 Type personType = assembly.GetType("ReflectionStudy.Model.Person"); //3、根据类型创建实体类 object? person = Activator.CreateInstance(personType); //3、获取属性 PropertyInfo ageInfo = personType.GetProperty("Age"); PropertyInfo nameInfo = personType.GetProperty("Name"); //4、属性赋值 nameInfo.SetValue(person,"张三"); ageInfo.SetValue(person,22); //打印 Console.WriteLine($"姓名:{nameInfo.GetValue(person)},年龄:{ageInfo.GetValue(person)}"); //5、获取方法 ////BindingFlags类型枚举,BindingFlags.NonPublic | BindingFlags.Instance 组合才能获取到private私有方法,不带此参数默认为public的方法 MethodInfo m1 = personType.GetMethod("TakeShower", BindingFlags.NonPublic | BindingFlags.Instance); MethodInfo m2 = personType.GetMethod("StaticWriteString"); MethodInfo m3 = personType.GetMethod("WriteString"); //6、执行方法 // public object? Invoke(object? obj, object?[]? parameters); //6.1不带参 m1.Invoke(person,null); //6.2 带一个个参 m2.Invoke(person, new string[]{"哈哈哈"}); //6.3 带多个参 string test = "爱在"; int i = 520; Object[] parametors = new Object[] {test, i}; m3.Invoke(person, parametors); }
执行结果:
//3、设置或获取属性的值、执行方法 { GetOrSetProperties(); //执行结果: //姓名:张三,年龄:22 //洗澡 //StaticWriteString:哈哈哈 //WriteString:爱在520 }
4、可利用反射+简单工厂+配置文件实现不同数据库的灵活切换和配置,见附件代码
博客后台 - 博客园 (cnblogs.com)