C#反射从入门到精通

大纲

  • 反射是什么
  • 为什么学习反射
  • 反射在哪个命名空间上
  • Type
  • Type常用属性和方法
  • 元数据是什么
  • 反射与Type和元数据之间的关系是什么?
  • 反射基本步骤
  • BindingFlags
  • 反射获取构造函数并实例对象
  • 反射获取对象方法并执行
  • 反射获取静态方法并执行
  • 加载程序集并查看程序集中类型

1.反射概念

什么是反射?

反射(Reflection)是.NET框架提供的一种强大机制,它允许程序在运行时动态获取类型信息、创建对象、调用方法和访问属性,而无需在编译时知道这些类型的具体信息

反射,就是这个让你能"透视"并操作这个黑盒子的X光机。

为什么要学习反射

为了实现可以让开发者,通过代码,解析dll文件exe程序中的内容。为了开发方便

1. 核心命/h名空间和类

复制代码
 System.Reflection - 反射功能的核心命名空间
// Type - 表示类型声明(类、接口、数组等)
// Assembly - 表示程序集,可加载和操作DLL/EXE文件

2.Type入门到精通

1.什么是type?

Type 是一个包含类型信息的类。它是访问元数据(Metadata)的主要入口,是进行反射操作的"钥匙"或"手柄"。

理解:每个数据类型都可以获取到Type, 反之 type对象也包含数据类型的所有信息:例如:类型名,成员

type能做什么?

通过Type 我们可以获取什么内容呢?

  • 获取类型名称
  • 获取类型的全部名称
  • 获取类型的成员:构造函数,字段,属性,方法,事件
  • 类型继承了谁,实现了哪些接口
  • 实例对象

2.获取type三种方式

复制代码
class Person{}
class UserInfo

方式一:适合已经获取到具体数据类型

复制代码
   // 方式1:通过 typeof 运算符(编译时已知类型)
   Type pType = typeof(Program);

方式二:适合已经知道类型的完成名称 并且引用到项目中

复制代码
   // 方式2:通过对象实例的 GetType() 方法(运行时获取)
   Person person = new Person();
   Type type = person.GetType();

方式三: 适合已经存在具体实例

复制代码
  // 方式3:通过类型名称字符串动态获取(非常常用!)
   ///Type.GetType("namespace.class");
   ///作用:根据类名,获取类的类型
   ///参数:完整的类型名(必须包含命名空间)
   Type pType1 = Type.GetType("_01反色.Person");

方式四:通过程序获取type 适合:只知道程序集和自己需要的类型命名和类型

复制代码
// 加载程序集
 Assembly assembly = Assembly.LoadFrom("dll文件路径");
// 获取程序集中所有TYpe
Type[] types = assembly.GetTypes();
// 获取特定的Type
Type stuType = assembly.GetType("Tools.Student");

3. Type探索属性。字段:函数

复制代码
  // 获取所有Public公共的属性
  var props = type.GetProperties();
  foreach (PropertyInfo pi in props)
  {
      Console.WriteLine(pi.Name+"  "+pi.PropertyType.Name);
      // 输出 Name  string
  }

 var pFields = type.GetFields();
 foreach (FieldInfo fi in pFields)
 {
     Console.WriteLine($"字段:{fi.Name} 类型:{fi.FieldType.Name}");
     // 输出 id int  包含系统自动生成
     // 输出 Num int
 }

var methods = type.GetMethods();
foreach (MethodInfo mi in methods)
{
	Console.WriteLine(mi.Name);
	// 输出:实例函数,继承函数,访问器get set
}

4.Type条件查询信息

复制代码
// 获取所有公共实例成员(包括继承的)
var flags = BindingFlags.Public | BindingFlags.Instance;

// 获取所有公共和非公共实例成员(包括私有字段和继承的)
var flags1 = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;

// 获取仅声明于当前类型的成员(排除继承的) + 公共实例
var flags2 = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;

// 获取静态成员
var flags3 = BindingFlags.Public | BindingFlags.Static;
// 私有的静态
var flags4 = BindingFlags.NonPublic | BindingFlags.Static;

// 切换变量查看结果         
var pFields = type.GetFields(flags);
foreach (FieldInfo fi in pFields)
{
    Console.WriteLine($"字段:{fi.Name} 类型:{fi.FieldType.Name}");
}
BindingFlags
BindingFlags是什么

BindingFlags:告诉你"要找什么"和"在哪找"。

BindingFlags 是一个枚举(Enum),用于在反射调用(如 Type.GetMethods(), Type.GetProperties() 等)时,指定要搜索的成员类型、可见性、作用域等筛选条件。

详解

基础标志

|-------------------------|------------|------------|
| 枚举值 | 作用 | 说明 |
| BindingFlags.Instance | 获取实例成员 | 对象。成员(非静态) |
| BindingFlags.Static | 获取静态成员 | 类名。成员 |

注意:这两个至少选择其中一个,必须写

访问权限的表示

|--------------------------|-------------|-------------------------------|
| 枚举值 | 作用 | 说明 |
| BindingFlags.Public | 获取公共成员 | public 修饰 |
| BindingFlags.NonPublic | 获取非公共成员 | private/protected/internal 修饰 |

反射常用组合

|---------------------------------|----------------|-----------------------|
| 枚举值 | 作用 | 适用场景 |
| BindingFlags.IgnoreCase | 忽略名称大小写 | 不区分大小写查找方法 / 属性 |
| BindingFlags.FlattenHierarchy | 展开层级获取静态成员 | 拿基类的 public static 成员 |
| BindingFlags.DeclaredOnly | 只拿当前类声明的成员 | 不获取继承的成员(只看自身) |

万能组合公式

需求 BindingFlags 组合

获取公共实例成员 Instance Public

获取私有实例成员 Instance NonPublic

获取公共静态成员 Static Public

获取私有静态成员 Static NonPublic

获取当前类自己的公共实例成员(不继承) Instance Public DeclaredOnly

5.Type属性和方法总结

属性

|-------------------|----------------------|
| 属性 | 作用 |
| Name | 获取类型的短名称(不含命名空间) |
| FullName | 获取类型的完整名称(含命名空间) |
| Namespace | 获取类型所属的命名空间 |
| BaseType | 获取类型的直接父类 |
| IsClass | 判断是否是 |
| IsInterface | 判断是否是接口 |
| IsEnum | 判断是否是枚举 |
| IsAbstract | 判断是否是抽象类型 |
| IsSealed | 判断是否是密封类 |
| IsPublic | 判断是否是公共类型 |
| IsGenericType | 判断是否是泛型类型 |
| Assembly | 获取类型所在的程序集 |

方法

  1. GetConstructors()
    作用:获取所有公共构造函数
    参数:无
    返回值:ConstructorInfo[]
    调用方式:type.GetConstructors()
  2. GetConstructor(Type[] types)
    作用:获取指定参数的公共构造函数
    参数:Type[] types(参数类型数组)
    返回值:ConstructorInfo
    调用方式:type.GetConstructor(new[] { typeof(int) })
  3. GetMethods()
    作用:获取所有公共方法(含继承)
    参数:无
    返回值:MethodInfo[]
    调用方式:type.GetMethods()
  4. GetMethod(string name)
    作用:获取指定名称公共方法
    参数:string name(方法名)
    返回值:MethodInfo
    调用方式:type.GetMethod("ToString")
  5. GetMethod(string name, Type[] types)
    作用:获取指定名称+参数的方法(匹配重载)
    参数:string name, Type[] types
    返回值:MethodInfo
    调用方式:type.GetMethod("Equals", new[] { typeof(object) })
  6. GetProperties()
    作用:获取所有公共属性
    参数:无
    返回值:PropertyInfo[]
    调用方式:type.GetProperties()
  7. GetProperty(string name)
    作用:获取指定名称公共属性
    参数:string name(属性名)
    返回值:PropertyInfo
    调用方式:type.GetProperty("Length")
  8. GetFields()
    作用:获取所有公共字段
    参数:无
    返回值:FieldInfo[]
    调用方式:type.GetFields()
  9. GetField(string name)
    作用:获取指定名称公共字段
    参数:string name(字段名)
    返回值:FieldInfo
    调用方式:type.GetField("MaxValue")
  10. GetEvents()
    作用:获取所有公共事件
    参数:无
    返回值:EventInfo[]
    调用方式:type.GetEvents()
  11. GetEvent(string name)
    作用:获取指定名称公共事件
    参数:string name(事件名)
    返回值:EventInfo
    调用方式:type.GetEvent("Click")
  12. GetInterfaces()
    作用:获取类型实现的所有接口
    参数:无
    返回值:Type[]
    调用方式:type.GetInterfaces()
  13. GetInterface(string name)
    作用:获取指定名称接口
    参数:string name(接口名)
    返回值:Type
    调用方式:type.GetInterface("IEnumerable")
  14. GetMembers()
    作用:获取所有公共成员(方法/属性/字段/事件等)
    参数:无
    返回值:MemberInfo[]
    调用方式:type.GetMembers()
  15. GetMember(string name)
    作用:获取指定名称的所有成员
    参数:string name(成员名)
    返回值:MemberInfo[]
    调用方式:type.GetMember("Length")
  16. GetCustomAttributes(bool inherit)
    作用:获取类型所有自定义特性
    参数:bool inherit(是否继承)
    返回值:object[]
    调用方式:type.GetCustomAttributes(true)
  17. GetCustomAttributes(Type attributeType, bool inherit)
    作用:获取指定类型的特性
    参数:Type attributeType, bool inherit
    返回值:object[]
    调用方式:type.GetCustomAttributes(typeof(ObsoleteAttribute), true)
  18. GetGenericArguments()
    作用:获取泛型类型参数
    参数:无
    返回值:Type[]
    调用方式:typeof(List).GetGenericArguments()
  19. GetGenericTypeDefinition()
    作用:获取泛型类型定义(如 List<>)
    参数:无
    返回值:Type
    调用方式:typeof(List).GetGenericTypeDefinition()
  20. GetElementType()
    作用:获取数组/指针元素类型
    参数:无
    返回值:Type
    调用方式:typeof(int[]).GetElementType()
  21. IsSubclassOf(Type parentType)
    作用:判断是否为某类子类(不含自身)
    参数:Type parentType
    返回值:bool
    调用方式:type.IsSubclassOf(typeof(object))
  22. IsAssignableFrom(Type c)
    作用:判断类型是否可赋值兼容
    参数:Type c
    返回值:bool
    调用方式:typeof(IEnumerable).IsAssignableFrom(typeof(string))
  23. IsInstanceOfType(object obj)
    作用:判断对象是否为该类型实例
    参数:object obj
    返回值:bool
    调用方式:type.IsInstanceOfType(obj)
  24. Equals(Type o)
    作用:判断是否为同一类型
    参数:Type o
    返回值:bool
    调用方式:type1.Equals(type2)

3.反射应用

1. 程序集加载

准备一个程序集.dll 中有 Class MenuInfo: MenuId MenuName

复制代码
// 方式1:通过程序集名称(不含后缀)
Assembly ass = Assembly.Load("Models");

// 方式2:通过文件绝对路径
Assembly ass = Assembly.LoadFile(@"D:\路径\Models.dll");

// 方式3:通过程序集文件名称(含后缀)
Assembly ass = Assembly.LoadFrom("Models.dll");

2.实例对象三种方式与动态创建

实例对象三种方式

复制代码
// 获取类型的Type对象
Type type = typeof(Person);

// ------------------三种创建实例的方式
// 直接实例化
Person user1 = new Person();  
// 泛型方式
Person p = Activator.CreateInstance<UserInfo>(); 
 // 通过Type对象
Person user3 = (Person)Activator.CreateInstance(type);

动态创建,

复制代码
// 1.动态创建对象
Type personType = Type.GetType("_01反色.Person");
object p = Activator.CreateInstance(personType); // 相当于 new Person()

3. 构造函数和实例对象

复制代码
// 获取所有公有构造函数
var cons = type.GetConstructors();

// 获取特定构造函数
ConstructorInfo cons1 = type.GetConstructor(new Type[] { }); // 无参构造
ConstructorInfo cons2 = type.GetConstructor(new Type[] { typeof(int), typeof(string) }); // 带参构造

// 使用构造函数创建实例
UserInfo user5 = (UserInfo)cons1.Invoke(null);
object user6 = cons2.Invoke(new object[] { 12, "Mr.Sun" });

4. 方法调用

实例方法调用语法总结
复制代码
MethodInfo fun = type.GetMethod("函数名");
------无参数无返回调用--------------
fun.Invoke(实例对象变量,null);

-------有参数无返回值-----------
fun.Invoke(实例对象变量,new object[] {实参1,实参2});

-------无参数有返回值----------
object res = fun.Invoke(实例对象变量,null);

---------有参数有返回值----------
object res = fun.Invoke(实例对象变量,new object[] {实参1,实参2});;

注意:有返回值的,将res 转化为指定的类型,
方法调用案例
复制代码
Type type = Type.GetType("_04反射调用方法.Student");

---- 有参构造函数-----
Type[] ctorPam = new Type[] { typeof(string), typeof(int) };
ConstructorInfo ctor = type.GetConstructor(ctorPam);
// 实例对象
Student stu = ctor.Invoke(new object[] { "张三", 18 }) as Student;

----------获取指定方法Show--------------
MethodInfo show = type.GetMethod("Show");
-------- 调用方法 无参数无返回值------------
show.Invoke(stu, null);

Console.WriteLine("------------------------");

---------获取指定方法SayHello--------------
MethodInfo sayHello = type.GetMethod("SayHello");
-------调用方法 有参数无返回值------
sayHello.Invoke(stu,new object[] { "李四"});

Console.WriteLine("------------------------");

// 获取指定方法Add 
MethodInfo add = type.GetMethod("Add");
-------调用方法 有参数有返回值----------
int res = (int)add.Invoke(stu,new object[] {1,2 });
Console.WriteLine(res);
Console.WriteLine("------------------------");

// 获取指定方法GetName
MethodInfo getName = type.GetMethod("GetName");
---------调用方法 无参数有返回值---------
string res1 = getName.Invoke(stu, null).ToString();
Console.WriteLine(res1);
静态方法调用语法总结
复制代码
MethodInfo fun = type.GetMethod("函数名");
------无参数无返回调用--------------
fun.Invoke(null,null);

-------有参数无返回值-----------
fun.Invoke(null,new object[] {实参1,实参2});

-------无参数有返回值----------
object res = fun.Invoke(null,null);

---------有参数有返回值----------
object res = fun.Invoke(null,new object[] {实参1,实参2});;

注意:有返回值的,将res 转化为指定的类型,
静态方法调用
复制代码
 Type type = typeof(Student);
 MethodInfo method = type.GetMethod("Show");
 method.Invoke(null, null);

 MethodInfo method1 = type.GetMethod("Show1");
 method1.Invoke(null, new object[] { 10 });

 MethodInfo method2 = type.GetMethod("Show2");
 int res = (int)method2.Invoke(null, new object[] { 10 });
 Console.WriteLine(res);


  class Student
  {
      public static void Show() { Console.WriteLine("静态方法"); }
      public static void Show1(int a) { Console.WriteLine("a=" + a); }

      public static int Show2(int a) { return a; }
  }

9. 实例属性的操作

  1. 根据属性名获取属性对象

  2. 给某个对象的属性赋值

  3. 查看某个对象属性赋值

    // 获取Age属性
    PropertyInfo proAge = type.GetProperty("Age");
    // stu.Age = 18
    proAge.SetValue(stu, 18);
    // stu.Age
    Console.WriteLine(proAge.GetValue(stu));

10.总结反射步骤

  1. 加载程序集
  2. 获取指定Type
  3. 通过Type探索元数据的结构
  4. 实例对象
  5. 操作实例对象属性
  6. 调用方法

4.元数据

元数据(Metadata)总结: 就是关于数据的数据。它本身不是核心内容,但它描述了核心内容的特征、属性、来源、关系等信息

反射与Type和元数据之间的关系是什么?

1:Type是反射的基石,是反射的钥匙

2:Type是通过元数据,才能获取类型中信息:类型名,有哪些成员

3:通过Type解析元数据的过程就是i反射,反射Reflection 中有 描述成员的具体实例类型。例如:

  • ContructorInfo 构造函数
  • PropertyInfo 属性
  • FildInfo 字段
  • MethodInfo 方法
相关推荐
daidaidaiyu2 小时前
一文学习入门 ThingsBoard 开源物联网平台
java·mqtt·spring
亚历克斯神2 小时前
Elasticsearch 全文搜索实战:构建企业级搜索引擎
java·spring·微服务
亚历克斯神2 小时前
Spring Boot 与 Elasticsearch 8.0 集成
java·spring·微服务
_深海凉_3 小时前
LeetCode热题100-除了自身以外数组的乘积
数据结构·算法·leetcode
a1117763 小时前
Three.js 的前端 WebGL 页面合集(日本 开源项目)
前端·javascript·webgl
Kk.08023 小时前
项目《基于Linux下的mybash命令解释器》(一)
前端·javascript·算法
星晨雪海3 小时前
Lombok 注解使用场景终极总结
java·数据库·mysql
SteveSenna3 小时前
Trossen Arm MuJoCo自定义1:改变目标物体
人工智能·学习·算法·机器人
程序员鱼皮3 小时前
又一个新项目开源,让 AI 帮你盯全网热点!
javascript·ai·程序员·编程·ai编程