第3章:反射
本章目标
掌握反射的原理
熟悉反射的基本运用
本章内容 反射是什么 C# 编译运行过程
首先我们在VS点击编译的时候,就会将C#源代码编译成程序集
程序集以可执行文件 (.exe) 或动态链接库文件 (.dll) 的形式实现
程序集中包含有Microsoft 中间语言 (MSIL) 和必需的元数据。
元数据存储以下信息:
程序集的说明:标识(名称、版本、区域性、公钥)、导出的类型、该程序集所依赖的其他程序集、运行所需的安全权限。
类型的说明:名称、可见性、基类和实现的接口、成员(方法、字段、属性、事件、嵌套的类型)。
特性:修饰类型和成员的其他说明性元素。
在执行时,实时 (JIT) 编译器将 MSIL 转换为本机代码
运行 Microsoft 中间语言 (MSIL) 前,必须根据公共语言运行时将其编译为目标计算机基础结构的本机代码。
运行代码
公共语言运行时提供启用要发生的托管执行的基础结构以及执行期间可使用的服务
反射的工作原理 反射 来自 System.Reflection命名空间,它可以读取程序集中的元数据,利用元数据创建对象,从而实现各种功能。
提示:
区分 反射 与反编译,反射读取的是元数据,反编译读取的IL代码
反射的优缺点
优点:提高了程序的灵活性和扩展性,降低耦合度
缺点:由于反射多了一道程序,性能上相较于直接代码要慢
反射的使用 反射相关的类和命名空间 反射的命名空间:
1 using System.Reflection;
反射相关的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 System.Type System.AppDomain System.Activator System.Reflection.Assembly System.Reflection.Module System.Reflection.ConstructorInfo System.Reflection.ParameterInfo System.Reflection.MethodInfo System.Reflection.PropertyInfo System.Reflection.FieldInfo System.Reflection.MemberInfo
Type类的应用 Type类中的基本属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 class Student { private int _num = 0 ; public string Phone = "15818704257" ; public string Name { get ; set ; } public string Address { get ; set ; } public Student () { } public Student (string name ) { } public Student (string name,string phone,string address ) { } public int PublicMethod () { return int .MinValue; } internal void InternalMethod () { } private void PrivateMethod () { } public void Show (int id ) { Console.WriteLine("调用了Show()方法!" +id); } } class MyArray <T >{ } interface IUsb { } struct Teacher{ }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 static void Fun1 (){ Type t0 = typeof (MyArray<int >); Type t1= typeof (Student); Type t2 = typeof (IUsb); Type t3 = typeof (Teacher); Type t4 = typeof (string ); Console.WriteLine("名称:" +t1.Name); Console.WriteLine("全名:" +t1.FullName); Console.WriteLine("命名空间:" +t1.Namespace); Console.WriteLine("是否是抽象的:" +t1.IsAbstract); Console.WriteLine("是否是公共的:" + t1.IsPublic); Console.WriteLine("是否是类:" + t1.IsClass); Console.WriteLine("是否是枚举:" + t1.IsEnum); Console.WriteLine("是否是接口:" + t1.IsInterface); Console.WriteLine("是否是嵌套定义的:" + t1.IsNested); Console.WriteLine("是否是值类型:" + t1.IsValueType); Console.WriteLine("是否是泛型类型:" + t1.IsGenericType); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 using ClassLibrary1;using System;using System.Collections.Generic;using System.Linq;using System.Reflection;using System.Text;using System.Threading.Tasks;namespace CH03Demo { internal class Program { static void Main (string [] args ) { Fun11(); Console.ReadLine(); } } }
提示:
FullName :获取该类型的完全限定名称,包括其命名空间,但不包括程序集 。
Type类的Assembly属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 static void Fun2 (){ Type type = typeof (Student); Assembly a1 = type.Assembly; Console.WriteLine("位置:" + a1.CodeBase); Console.WriteLine("全名:" + a1.FullName); Console.WriteLine("--------------------------------" ); Type t1 = typeof (Student); Type t2 = typeof (string ); Console.WriteLine("全名:" + t1.FullName); Console.WriteLine("全名:" + t2.FullName); Console.WriteLine(t1.AssemblyQualifiedName); Console.WriteLine(t2.AssemblyQualifiedName); }
Type类对象获取构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static void Fun3 (){ Type t= typeof (Student); ConstructorInfo[] ciArray= t.GetConstructors(); foreach (ConstructorInfo ci in ciArray) { Console.WriteLine("构造函数名:" + ci.Name); foreach (ParameterInfo item in ci.GetParameters()) { Console.WriteLine("参数:{0},类型:{1}" , item.Name, item.ParameterType); } Console.WriteLine("-------------------------------" ); } }
Type类对象获取方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static void Fun4 (){ Type t = typeof (Student); MethodInfo[] miArray = t.GetMethods(); foreach (MethodInfo mi in miArray) { Console.WriteLine("方法名:{0},返回类型:{1}" ,mi.Name,mi.ReturnType); foreach (ParameterInfo item in mi.GetParameters()) { Console.WriteLine("参数:{0},类型:{1}" ,item.Name,item.ParameterType); } Console.WriteLine("-------------------------------" ); } }
Type类对象获取属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 static void Fun5 (){ Type t = typeof (Student); PropertyInfo[] miArray = t.GetProperties(); foreach (PropertyInfo pi in miArray) { Console.WriteLine("属性名:{0},类型:{1}" ,pi.Name,pi.PropertyType); Console.WriteLine("-------------------------------" ); } }
Type类对象获取字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 static void Fun6 (){ Type t = typeof (Student); FieldInfo[] fiArray = t.GetFields(); foreach (FieldInfo fi in fiArray) { Console.WriteLine("字段名:{0},类型:{1}" , fi.Name, fi.FieldType); Console.WriteLine("-------------------------------" ); } }
Type类对象获取成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 static void Fun7 (){ Type t = Type.GetType("CH03Demo.Student" ); MemberInfo[] miArray = t.GetMembers(); foreach (MemberInfo mi in miArray) { Console.WriteLine("成员名:{0},类型:{1}" , mi.Name, mi.MemberType); Console.WriteLine("-------------------------------" ); } }
使用BindingFlags筛选成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 static void Fun8 (){ Type t = Type.GetType("CH03Demo.Student" ); MemberInfo[] miArray = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance); foreach (MemberInfo mi in miArray) { Console.WriteLine("成员名:{0},类型:{1}" , mi.Name, mi.MemberType); Console.WriteLine("-------------------------------" ); } }
提示:
BindingFlags.Instance 和BindingFlags.Static :实例成员是相对于静态成员而言的,多数情况下我们都省略了BindingFlags 这个参数,少数需要筛选成员的时候,传入该参数
获取Type实例的方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static void Fun9 (){ Student stu = new Student(); Type t1 = typeof (Student); Type t2 = stu.GetType(); Type t3 = Type.GetType("CH03Demo.Student" ); Type t4 = Type.GetType("ClassLibrary1.Class1,ClassLibrary1" ); }
Activator类的应用 1 2 3 Type type = typeof (UserInfo); UserInfo userInfo=(UserInfo)Activator.CreateInstance(type);
Assembly类的应用
对于程序集的限定名称使用小结
程序集的显示名称,可通过Assembly.FullName
和 Assembly.GetName().FullName(即AssemblyName.FullName)
两种方式获取,这种获取的名称,一般是作为 Assembly.Load()
的标准参数值
类型的程序集限定名,可通过Type类中的AssemblyQualifiedName
属性获取(通常作为Type.GetType()方法中的参数值), 相较于Assembly.FullName
,名称格式上多了 Type.FullName 这一部分
Assembly类中的常用方法
Assembly.Load()方法接收一个String或AssemblyName类型作为参数,这个参数需要程序集的强名称
Assembly.LoadFrom() 根据程序集的文件名或路径,加载程序集;这个方法会加载此程序集引用的其他程序集
Assembly.LoadFile() 加载指定路径上的程序集文件内容,和上面方法的不同之处是这个方法不会加载此程序集引用的其他程序集
程序集的强名称:是程序集的FullName(具有名称,版本,语言,公钥标记); 程序集的弱命名:只有程序集名称而没有版本,语言和公钥标记;平常我们创建的一个类库,如果没有特殊操作都属于是是弱名称程序集 Load(“强名称程序集”)查找程序集的顺序:首先它会去全局程序集缓存查找,然后到应用程序的根目录查找,最后会到应用程序的私有路径查找。 Load(“弱名称程序集”)查找程序集的顺序:首先到应用程序的根目录查找,最后会到应用程序的私有路径查找。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 static void Fun12 (){ Assembly assembly1 = Assembly.Load("ClassLibrary1" ); Assembly assembly2 = Assembly.LoadFrom("ClassLibrary1.dll" ); Assembly assembly3 = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory+ "ClassLibrary1.dll" ); }
Module类的应用 什么是模块 System.Reflection.Module类 是C#中的一个重要类,它提供了有关程序集中模块的信息和功能。这个类的作用和使用方法在深入探讨模块类时会被详细讨论,通过一些示例代码来展示它的功能。在C#中,模块可以是一个源代码文件、一个编译后的文件(DLL或EXE)或者一个动态生成的程序集。每个模块都有自己的元数据和IL代码,而System.Reflection.Module类提供了访问和操作这些模块的能力。通过Module类,我们可以获取模块的元数据、类型信息、成员信息以及执行模块中的代码。
模块的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 static void Fun13 (){ Type t1 = typeof (Student); Type t2 = typeof (string ); Module module1 = t1.Module; Module module2 = t2.Module; Console.WriteLine("模块名:" +module1.Name); Console.WriteLine("完全限定名:" + module1.FullyQualifiedName); Console.WriteLine("程序集名:" + module1.Assembly.FullName); Console.WriteLine("-----------------------------" ); Console.WriteLine("模块名:" + module2.Name); Console.WriteLine("完全限定名:" + module2.FullyQualifiedName); Console.WriteLine("程序集名:" + module2.Assembly.FullName); }
AppDomain类的应用
前提:
从.NET Core开始,不再支持运行时创建其他AppDomain(即仅可在.NET Framework下支持创建其他AppDomain)。
因此以下部分内容仅在.NET Framework上有效。
官方说明:.NET Framework 技术在 .NET 6 及更高版本上不可用
AppDomain应用程序域:一组程序集的逻辑容器,CLR创建的第一个AppDomain称为默认AppDomain,仅在进程终止时销毁。
一个AppDomain可以包含N个Assembly,一个Assembly可以包含N个Module,而一个Module可以包含N个Type.
1 2 3 4 5 6 7 8 9 10 11 12 13 static void Fun14 (){ Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Console.WriteLine(assembly.FullName); } }
通过反射创建对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 static void Fun10 (){ Type t = typeof (Class1); #region 方式1:通过Invoke 执行构造函数 ConstructorInfo ci1 = t.GetConstructor(new Type[] { }); ConstructorInfo ci2 = t.GetConstructor(new Type[] {typeof (int ) }); object obj1= ci1.Invoke(new object [] { }); object obj2 = ci2.Invoke(new object [] {15 }); Class1 c1=obj1 as Class1; Class1 c2 = obj2 as Class1; #endregion #region 方式2:通过Assembly 创建实例 Assembly assembly = Assembly.Load("ClassLibrary1" ); object obj3 = assembly.CreateInstance("ClassLibrary1.Class1" ,true ); Class1 c3= obj3 as Class1; #endregion #region 方式3:通过 Activator 创建实例 object obj4 = Activator.CreateInstance(t); object obj5 = Activator.CreateInstance("ClassLibrary1" , "ClassLibrary1.Class1" ); Class1 c4 = obj4 as Class1; Class1 c5 = obj5 as Class1; #endregion }
通过反射获取对象成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 static void Fun11 (){ Type t = typeof (Student); object obj= Activator.CreateInstance(t); #region 反射:获取方法 MethodInfo mi= t.GetMethod("Show" ); mi.Invoke(obj, new object [] {25 }); #endregion #region 反射:获取属性 PropertyInfo pi = t.GetProperty("Name" ); pi.SetValue(obj, "张三" ); string name= pi.GetValue(obj) as string ; Console.WriteLine("姓名:" +name); #endregion #region 反射:获取字段 FieldInfo fi = t.GetField("Phone" ); fi.SetValue(obj, "13523983345" ); string phone = fi.GetValue(obj) as string ; Console.WriteLine("电话:" + phone); #endregion }
反射的应用 数据库辅助类反射 常规情况下:编写固定DBHelper类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 using System;using System.Collections.Generic;using System.Data.SqlClient;using System.Data;using System.Linq;using System.Text;using System.Threading.Tasks;namespace CH03Demo { internal class DBHelper { private static readonly string _connectionString = "server=.;database=test;uid=sa;pwd=sa" ; public int ExecuteNonQuery (string sql,params SqlParameter[] parameters ) { return 0 ; } public DataTable ExecuteTable (string sql,params SqlParameter[] parameters ) { return null ; } } }
反射+配置文件动态实现 1.创建一个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 using System;using System.Collections.Generic;using System.Data.SqlClient;using System.Data;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Data.Common;namespace CH03Demo { internal interface IDBHelper { int ExecuteNonQuery (string sql, params DbParameter[] parameters ) ; DataTable ExecuteTable (string sql, params DbParameter[] parameters ) ; } }
2.实现接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 using System;using System.Collections.Generic;using System.Data;using System.Data.Common;using System.Linq;using System.Text;using System.Threading.Tasks;namespace CH03Demo { internal class SqlServerDBHelper : IDBHelper { public int ExecuteNonQuery (string sql, params DbParameter[] parameters ) { return 0 ; } public DataTable ExecuteTable (string sql, params DbParameter[] parameters ) { return null ; } } }
3.增加配置文件
1 2 3 4 5 6 7 8 9 <?xml version="1.0" encoding="utf-8" ?> <configuration > <startup > <supportedRuntime version ="v4.0" sku =".NETFramework,Version=v4.7.2" /> </startup > <appSettings > <add key ="DBType" value ="SqlServer" /> </appSettings > </configuration >
4.过反射+配置文件 调用 数据库执行语句的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 static void Fun15 (){ string fullName = ConfigurationManager.AppSettings["DBType" ].ToString(); IDBHelper dbHelper = (IDBHelper)Assembly.Load("DBHelper" ).CreateInstance(fullName); var data1 = dbHelper.ExecuteNonQuery("delete from student where studentNo=@studentNo" , new SqlParameter("@studentNo" , "GCKJ101" )); var data2 = dbHelper.ExecuteTable("select * from student" ); Console.WriteLine(data1); Console.WriteLine(data2.Rows.Count); }
目录结构:
本章总结 略
课后作业 1.通过反射查看int类 Type基本信息 2.通过反射调用int类的实例方法: CompareTo 3.通过反射调用int类的静态方法: Parse