【C#进阶】C# 反射

news/2024/11/7 5:41:37/
序号系列文章
11【C#基础】C# 预处理器指令
12【C#基础】C# 文件与IO
13【C#进阶】C# 特性

文章目录

  • 前言
  • 1,反射的概念
  • 2,使用反射访问特性
  • 3,反射的用途
  • 4,反射的优缺点比较
    • 4.1 优点:
    • 4.2 缺点:
  • 5,System.Reflection 命名空间
    • 5.1 获取程序集中的信息
    • 5.2 获取程序集中的类型
    • 5.3 获取程序集中的成员
    • 5.4 创建类型的实例对象
  • 结语

前言

✋ 大家好,我是writer桑,本章为大家介绍 C# 中的反射


1,反射的概念

反射指的是程序可以访问,检测和修改它本身状态或行为的一种行为。 其中访问的目标包括程序集1、模块和类型对象等。可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型,然后调用其方法或访问器字段和属性。反射行为也常常用来访问应用在程序上的特性。

代码示例:(简单展示)

using System;
using System.Reflection;public class Example
{static void Main(){// 使用反射获取程序集的信息:Assembly info = typeof(int).Assembly;Console.WriteLine(info);}
}

运行结果:
在这里插入图片描述
在上例中,使用 typeof 方法获取已加载的 int 程序集的完整名称。

2,使用反射访问特性

在前面的一章中,我们讨论到了 C# 使用反射访问特性的操作。在本章中就可以使用反射的操作来访问 Example 类中的元数据。

代码示例:

using System;[System.AttributeUsage(System.AttributeTargets.Class |System.AttributeTargets.Struct)
]
public class AuthorAttribute : System.Attribute
{public string name;		public double age;// 构造函数public AuthorAttribute(string name){this.name = name;age = 11;}
}[Author("writer桑", age = 21)]
public class Example
{// Example类成员
}public class Program
{static void Main(){// 访问 Example 类应用的特性             Type type = typeof(Example);// 遍历输出 foreach(Object attributes in type.GetCustomAttributes(true)){AuthorAttribute author = (AuthorAttribute)attributes;Console.WriteLine(author.age);Console.WriteLine(author.name); }}
}

运行结果:
在这里插入图片描述

3,反射的用途

反射的用途可以总结为以下几点:

  1. 需要访问程序中的元数据的特性时。(点击了解更多)
  2. 检查和实例化程序集中的类型时。
  3. 在运行时构建新的类型,比如使用 System.Reflection.Emit 中的类。(点击了解更多)
  4. 执行后期绑定,访问在运行时创建的类型上的方法时。(点击了解更多)

4,反射的优缺点比较

反射的优缺点比较:

4.1 优点:

  • 反射是运行期的操作,提高了程序的灵活性和扩展性。
  • 降低耦合度2,提高自适应能力。
  • 允许动态的创建和使用对象,无需提前硬编码3目标对象。

4.2 缺点:

  • 性能较低,反射是运行期4的代码,在性能方面不如直接的编译型5代码。
  • 可读性较低,使用反射会使得程序本身的逻辑变得复杂,在可读性方面不如直接的代码来的简洁。
  • 维护成本变高,反射是一种绕过了源代码的技术, 因而在维护的方面难度较高。

5,System.Reflection 命名空间

System.Reflection 命名空间中包含通过检测托管代码中程序集、模块、成员、参数和其他实体的元数据来检索其相关信息的类型。同时也可以用于操作加载类型的实例,例如钩子函数6或调用方法。在 C# 中,实现反射操作常常需要用到 System.Reflection 命名空间中的类和方法。

列举一些 System.Reflection 命名空间中常用的反射操作:

5.1 获取程序集中的信息

System.Reflection 命名空间中的 Assembly.FullName 等属性可以用来获取程序集中的信息。

代码示例:

using System;
using System.Reflection;public class Example
{private int factor;public Example(int f){factor = f;}public int SampleMethod(int x){Console.WriteLine($"实例方法的执行:({x})");return x * factor;}public static void Main(){Assembly assem = typeof(Example).Assembly;Console.WriteLine($"程序集的全程:{assem.FullName}");// 可以使用 AssemblyName 类型解析完整名称。AssemblyName assemName = assem.GetName();Console.WriteLine($"名称: {assemName.Name}");//从程序集创建一个对象,并传入正确的数字//构造函数的参数类型。Object o = assem.CreateInstance("Example", false, BindingFlags.ExactBinding, null, new Object[] { 2 }, null, null);// 对对象的实例方法进行晚绑定调用。MethodInfo m = assem.GetType("Example").GetMethod("SampleMethod");Object ret = m.Invoke(o, new Object[] { 42 });Console.WriteLine($"示例方法的返回值为:{ret}");Console.WriteLine($"程序集入口点:{assem.EntryPoint}");}
}

运行结果:
在这里插入图片描述

5.2 获取程序集中的类型

System.Reflection 命名空间中的 Assembly.GetExportedTypes 方法可以用来获取程序集中定义的公共类型,这些公共类型在程序集外可见。

代码示例:

using System;
using System.Reflection;public class PublicClass
{public class PublicNestedClass { }protected class ProtectedNestedClass { }internal class FriendNestedClass { }private class PrivateNestedClass { }
}public class Example
{public static void Main(){// 获取程序集中定义的公共类型 foreach (Type t in typeof(Example).Assembly.GetExportedTypes()){Console.WriteLine(t);}}
}

运行结果:
在这里插入图片描述

5.3 获取程序集中的成员

System.Reflection 命名空间中的 MemberInfo 类中的属性和方法可以用来获取有关成员属性的信息并提供对成员元数据的访问权限。

代码示例:

using System;
using System.Reflection;public class Example
{// 显示应用到指定成员的自定义属性 public static void DisplayAttributes(Int32 indent, MemberInfo mi){// 获取自定义属性集;如果不存在,则返回。object[] attrs = mi.GetCustomAttributes(false);if (attrs.Length == 0) { return; }// 显示应用于该成员的自定义属性。Display(indent + 1, "属性:");foreach (object o in attrs){Display(indent + 2, "{0}", o.ToString());}}// 显示一个缩进的格式化字符串。 public static void Display(Int32 indent, string format, params object[] param){Console.Write(new string(' ', indent * 2));Console.WriteLine(format, param);}public static void Main(){//该变量保存缩进的数量, 显示每一行信息时使用。Int32 indent = 0;// 显示加载到这个 AppDomain 第一个 程序集的信息。Assembly b = AppDomain.CurrentDomain.GetAssemblies()[0];Display(indent, "程序集: {0}", b);// 显示从此程序集导出的 第一个 类型的相关信息。 indent += 1;Type t = b.GetExportedTypes()[0];Display(0, "");Display(indent, "类型: {0}", t);// 遍历显示成员及其自定义属性。indent += 1;foreach (MemberInfo mi in t.GetMembers())       // GetMembers 方法{Display(indent, "成员: {0}", mi.Name);DisplayAttributes(indent, mi);// 如果成员是一个方法,显示它的参数信息。if (mi.MemberType == MemberTypes.Method){foreach (ParameterInfo pi in ((MethodInfo)mi).GetParameters()){Display(indent + 1, "参数: 类型={0}, 名字={1}", pi.ParameterType, pi.Name);}}// 如果成员是一个属性,显示关于属性的访问方法的信息。 if (mi.MemberType == MemberTypes.Property){foreach (MethodInfo am in ((PropertyInfo)mi).GetAccessors()){Display(indent + 1, "访问器方法: {0}", am);}}}}
}

运行结果:
在这里插入图片描述

5.4 创建类型的实例对象

System.Reflection 空间中的 Assembly.CreateInstance 方法可以用来获取包含当前执行的代码的程序集,以此来创建类型的实例对象。

代码示例:

using System;
using System.Reflection;public class Person
{private string _name;public Person(){ }public Person(string name){this._name = name;}public string Name{get { return this._name; }set { this._name = value; }}public override string ToString(){return this._name;}
}public class Example
{public static void Main(){Assembly assem = typeof(Person).Assembly;// 创建 Person 类的实例化对象 Person p = (Person)assem.CreateInstance("Person");if (!(p == null)){p.Name = "John";Console.WriteLine($"实例化值为'{p}'的{p.GetType().Name}对象");}else{Console.WriteLine("无法实例化Person对象。");}}
}

运行结果:
在这里插入图片描述

点击了解更多 System.Reflection 命名空间的使用。


结语

👋 以上就是关于 C# 反射的介绍啦,希望对大家有所帮助。感谢大家的支持。


  1. 程序集(assembly):是一个及一个以上托管模块,以及一些资源文件的逻辑组合。在 .NET 中,dll与exe文件都是程序集。 ↩︎

  2. 耦合性(或称耦合力或耦合度):是一种软件度量,是指一程序中,模块及模块之间信息或参数依赖的程度。 ↩︎

  3. 硬编码:是将数据直接嵌入到程序或其他可执行对象的源代码中的软件开发实践,与从外部获取数据或在运行时生成数据不同。 ↩︎

  4. 运行期:是把编译后的文件交给计算机执行,直到程序运行结束。 ↩︎

  5. 编译期:是指把源码交给编译器编译成计算机可以执行的文件的过程。 ↩︎

  6. 钩子函数:是 Windows 消息处理机制的一部分,是指在程序正常运行中接受信息之前预先启动的函数,用来检查和修改传给该程序的信息,(钩子)实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。 ↩︎


http://www.ppmy.cn/news/31716.html

相关文章

Jetpack Compose 学习汇总

关于 Jetpack Compose 的学习本想只是简单的快速学习一下,结果万万没想到,竟然一下子折腾了好几个月。。。 下面将之前记录的 Jetpack Compose 相关的学习博文进行一个汇总链接整理,方便我以后自己查阅,也希望能帮到一些有正在学…

Web漏洞-XXE漏洞(详细)

XXE漏洞XXE全称为XML External Entity Injection即XMl外部实体注入漏洞原理:XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载, 导致用户可以控制外部的加载文件,造成XXE漏洞。XXE漏洞触发点往往是可以上传xml文件的位…

实现VOC数据集与COCO数据集格式转换

实现VOC数据集与COCO数据集格式转换2、将voc数据集的xml转化为coco数据集的json格式2、COCO格式的json文件转化为VOC格式的xml文件3、将 txt 文件转换为 Pascal VOC 的 XML 格式<annotation><folder>文件夹目录</folder><filename>图片名.jpg</file…

线程等待/休眠/状态及 Runnable 和 Callable 的简单使用及原理

关于线程和进程的基本概念☛操作系统中线程和进程的概念理解 这篇文章已经有了很详细的解释, 接下来主要来讲讲线程等待与线程休眠 / 线程的几种状态 / Runnable 和 Callable 与 Thread 的概念和区别及 Executor 框架是什么样的. 关于线程1 线程等待与线程休眠2 线程一共有哪些…

金三银四、金九银十 面试宝典 MySQL面试题 超级无敌全的面试题汇总(超万字的面试题,让你的MySQL无可挑剔)

MySQL数据库 - 面试宝典 又到了 金三银四、金九银十 的时候了&#xff0c;是时候收藏一波面试题了&#xff0c;面试题可以不学&#xff0c;但不能没有&#xff01;&#x1f941;&#x1f941;&#x1f941; 一个合格的 计算机打工人 &#xff0c;收藏夹里必须有一份 MySQL 八…

计及需求响应的粒子群算法求解风能、光伏、柴油机、储能容量优化配置(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

蓝桥杯刷题第七天

第一题&#xff1a;三角回文数问题描述对于正整数 n, 如果存在正整数 k 使得2n123⋯k2k(k1), 则 n 称为三角数。例如, 66066 是一个三角数, 因为 66066123⋯363。如果一个整数从左到右读出所有数位上的数字, 与从右到左读出所有数位 上的数字是一样的, 则称这个数为回文数。例如…

Golang管理依赖关系

当您的代码使用外部包时&#xff0c;这些包&#xff08;作为模块分发&#xff09;成为依赖项。随着时间的推移&#xff0c;您可能需要升级或更换它们。Go 提供了依赖项管理工具&#xff0c;可帮助您在合并外部依赖项时确保 Go 应用程序的安全。本主题描述如何执行任务来管理您在…