C# 图解教程 第5版 —— 第25章 反射和特性

文章目录

    • 25.1 元数据和反射
    • 25.2 Type 类
    • 25.3 获取 Type 对象
    • 25.4 什么是特性
    • 25.5 应用特性
    • 25.6 预定义的保留特性
      • 25.6.1 Obsolete 特性
      • 25.6.2 Conditional 特性
      • 25.6.3 调用者信息特性
      • 25.6.4 DebuggerStepThrough 特性
      • 25.6.5 其他预定义特性
    • 25.7 关于应用特性的更多内容
      • 25.7.1 多个特性
      • 25.7.2 其他类型的目标
      • 25.7.3 全局特性
    • 25.8 自定义特性
      • 25.8.1 声明自定义特性
      • 25.8.2 使用特性的构造函数
      • 25.8.3 指定构造函数
      • 25.8.4 使用构造函数
      • 25.8.5 构造函数中的位置参数和命名参数
      • 25.8.6 限制特性的使用
      • 25.8.7 自定义特性的最佳实践
    • 25.9 访问特性
      • 25.9.1 使用 IsDefined 方法
      • 25.9.2 使用 GetCustomAttributes 方法

25.1 元数据和反射

​ 有些程序处理的数据不是数字、文本或图形,而是关于程序和程序类型的信息。

  • 有关程序及其类型的数据被称为元数据,保存在程序的程序集中。

  • 程序运行时,可以查看其他程序集或其本身的元数据。这种行为叫做反射

  • 要使用反射,必须使用 System.Reflection 命名空间。

25.2 Type 类

​ BCL 声明了一个 Type 抽象类(不能有实例),用来包含类型的特征,获取程序使用的类型信息。

​ 在运行时,CLR 创建从 Type 派生的类(RuntimeType)的实例,包含类型信息。访问这些实例时,CLR 不会返回派生类的引用,而是返回 Type 类的引用。方便起见,本章将引用指向的对象称为 Type 类型的对象。

  • 程序中用到的每一个类型,CLR 都会创建一个包含这个类型信息的 Type 类型对象。
  • 同一类型的所有实例只被一个 type 对象关联。
image-20240114140313995
图25.1 对于程序中使用的每个类型,CLR 都会实例化 Type 类型的对象

​ 表 25.1 列出了 Type 类中常用的成员。

表25.1 System.Type 类的部分成员
image-20240114140613155

25.3 获取 Type 对象

使用 GetType 方法

​ object 类型包含方法 GetType,返回示例的 Type 对象引用。由于每个类型都是由 object 派生的,因此可以在任何类型对象上使用 GetType 方法。

image-20240114141117010

使用 typeof 运算符

​ 提供类型名作为操作数,typeof 就会返回 Type 对象的引用。

image-20240114141206405

25.4 什么是特性

​ 特性是一种允许向程序集添加元数据的语言结构,用于保存程序结构信息的特殊类型。

  • 将应用了特性的程序结构称为目标
  • 设计用来获取和使用元数据的程序称为特性的消费者
  • .NET 预定义了许多特性,也可以自己声明自定义特性。
image-20240114141409949
图25.2 创建和使用特性的相关组件
  • 在源代码中将特性应用于程序结构。
  • 编译器获取源代码并从特性产生元数据,之后将元数据放到程序集中。
  • 消费者程序可以获取特性的元数据以及程序中其他组件的元数据。即,编译器同时生产和消费特性。
  • 特性名使用 Pascal 命名法并以 Attribute 后缀结尾。

25.5 应用特性

  • 通过在结构前防止特性片段来应用特性。
  • 特性片段由方括号包围特性名和参数列表(可有可无)构成。
  • 大多数特性只应用于直接跟随在一个或多个特性片段后的结构。
  • 引用了特性的结构称为被特性装饰(decorated 或 adorned)。
image-20240114141747207

25.6 预定义的保留特性

25.6.1 Obsolete 特性

​ 使用 Obsolete 特性将程序结构标注为“过时”,并可以提供相关的警告信息。

image-20240114142007760

​ 程序可以正常运行,但是编译器会产生一条警告信息:

image-20240114142047163

​ 另外,可以通过改变第二个参数为 true,将代码标记为错误而不是警告。

image-20240114142120814

25.6.2 Conditional 特性

​ Conditional 特性允许包括或排斥指定方法的所有调用,使用该特性时,需要将一个编译符号作为参数。

  • 如果定义了编译符号,则编译器会包含所有调用这个方法的代码。
  • 如果没有定义编译符号,编译器将忽略代码中这个方法的所有调用。
  • 方法本身的 CIL 代码会包含在程序集中,只是调用时会被忽略。
  • 除了应用在方法上,Conditional 特性还可以引用在类上,这里不做介绍。

​ Conditional 特性应用的方法必须满足以下条件:

  1. 必须是类或结构体的方法。
  2. 必须为 void 方法。
  3. 不能被声明为 override,但可以是 virtual。
  4. 不能是接口方法的实现。
image-20240114142543690

​ 当编译器编译上述代码时,会检查是否定义了编译符号 DoTrace。

  • 若定义,则编译器和往常一样包含这些方法的调用。
  • 若未定义,则编译器不会输出任何对 TraceMessage 的任何调用代码。

25.6.3 调用者信息特性

​ 利用调用者信息特性可以访问文件路径、代码行数、调用成员的名称等源代码信息。

  • 这三个特性分别为:
    1. CallerFilePath。
    2. CallerLineNumber。
    3. CallerMemberName。
  • 上述特性只能用于方法中的可选参数。
image-20240114142852125 image-20240114142900158

25.6.4 DebuggerStepThrough 特性

​ DebuggerStepThrough 特性告诉调试器在执行目标代码时不要进入该方法调试。

  • 该特性位于 Sustem.Diagnostics 命名空间。
  • 该特性可用于类、结构、构造函数、方法或访问器。

25.6.5 其他预定义特性

表25.2 .NET 中定义的重要特性
image-20240114143102018

25.7 关于应用特性的更多内容

25.7.1 多个特性

​ 可以为单个结构应用多个特性。

  • 多个特性可以使用下面任何一种格式列出:
    • 独立的特性片段。
    • 单个特性片段,特性之间使用逗号分隔。
  • 可以以任何次序列出特性。
image-20240114143251070

25.7.2 其他类型的目标

​ 可以将特性应用到其他程序结构,并可以显示地标注特性。

image-20240114143345434
表25.3 特性目标
image-20240114143354899

25.7.3 全局特性

​ 可以使用 assembly 和 module 目标名称来使用显式目标说明符将特性设置在程序集或模块级别。

  • 程序集级别的特性必须防止在任何命名空间之外,并且通常放置在 AssemblyInfo.cs 文件中。
  • AssemblyInfo.cs 文件通常包含有关公司、产品以及版权信息的元数据。
image-20240114143649490 image-20240114143703590

25.8 自定义特性

​ 特性只是一种特殊的类:

  • 用户自定义的特性类称为自定义特性。
  • 所有特性类都派生自 System.Atrribute。

25.8.1 声明自定义特性

  • 声明一个自定义特性,需要做如下工作:
    • 声明一个派生自 System.Attribute 的类。
    • 起一个以后缀 Attribute 结尾的名称。
  • 安全起见,建议声明的特性类为 sealed。
  • 由于特性持有目标的信息,所有特性类的公有成员只能是:
    • 字段。
    • 属性。
    • 构造函数。
image-20240114143946786

25.8.2 使用特性的构造函数

​ 每个特性必须至少有一个公共构造函数。

  • 和其他类一样,如果不声明构造函数,编译器会产生一个隐式公共无参的构造函数。
  • 特性的构造函数和其他构造函数一样,可以被重载。
  • 声明构造函数时必须使用类全名,包括后缀。只可以在应用特性时使用短名称。
image-20240114144154829

25.8.3 指定构造函数

​ 在为目标应用特性时,其实在指定应该使用哪个构造函数来创建特性实例。

image-20240114144258315
  • 应用特性时,构造函数的实参必须在编译时就能确定值。
  • 如果应用的特性构造函数没有参数,可以省略圆括号。
image-20240114144342040

25.8.4 使用构造函数

​ 和其他类一样,不能显式调用构造函数。特性的实例被创建后,只有特性的消费者访问特性时才能调用构造函数。因此,应用一个特性是一条声明语句,只决定使用哪个构造函数创建特性,而不会当即创建特性。

  • 命令语句的意义是:“在这里创建新的类”。
  • 声明语句的意义是:“这个特性和这个目标相关联,如果需要创建特性,则使用这个构造函数”。
image-20240114144618792
图25.3 比较构造函数的使用

25.8.5 构造函数中的位置参数和命名参数

​ 与普通类的方法和构造函数蕾西,特性的构造函数同样可以使用位置参数和命名参数,且位置参数必须放在命名参数之前。

image-20240114144818907

25.8.6 限制特性的使用

​ 使用预定义特性 AttributeUsage 来限制自定义特性的使用范围。

image-20240114144957407

​ AttributeUsage 有 3 个重要的公有属性,如表 25.4 所示。

表25.4 AttributeUsage 的公有属性
image-20240114145117427

AttributeUsage 的构造函数

​ AttributeUsage 的构造函数接受单个位置参数,该参数设置 ValidOn 属性,指定可使用特性的目标类型。

image-20240114145754641

​ 可接受的目标类型是 AttributeTargets 枚举的成员,枚举的所有成员如表 25.5 所示。

表25.5 AttributeTargets 枚举的成员
image-20240114145740137

​ 下面示例中的 MyAttribute 只能应用在类上,却不会被应用它的类的派生类继承。

image-20240114145958787

25.8.7 自定义特性的最佳实践

​ 建议参考如下示例编写自定义特性:

  1. 特性类应明确表示目标结构的某种状态。
  2. 除了属性外,不要实现共有方法或其他函数成员。
  3. 为了更安全,将特性类声明为 sealed。
  4. 在特性声明中使用 AttributeUsage 来显式指定特性目标组。
image-20240114150021225

25.9 访问特性

​ 使用 Type 对象的方法来获取自定义特性。

25.9.1 使用 IsDefined 方法

  • 第一个参数接受需要检查的特性的 Type 对象。
  • 第二个参数为 bool 类型,指示是否搜索 MyClass 继承树来查找该特性。
image-20240114150450998 image-20240114150459008

25.9.2 使用 GetCustomAttributes 方法

  • 实际返回的对象是 object 数组,因此必须将其强制转换为相应的特性类型。
  • 布尔参数指定是否搜索继承树来查找特性。
image-20240114150617461
  • 调用 GetCustomAttributes 方法后,每个与目标关联的特性示例就会被创建。
image-20240114150814081 image-20240114150707277 image-20240114150828681

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/416061.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

谷达冠楠科技:抖音开网店新手小白可以卖的产品

随着互联网的发展,越来越多的人选择在网上开设自己的店铺。而抖音作为目前最火的短视频平台,也提供了开店的功能。那么,对于新手小白来说,抖音开网店可以卖哪些产品呢? 我们可以考虑的是服装类商品。抖音上有很多时尚博主&#x…

【华为 ICT HCIA eNSP 习题汇总】——题目集1

1、(多选)根据下面所示的命令输出,下列描述中正确的是? A、GigabitEthernet0/0/1 允许VLAN1通过 B、GigabitEthernet0/0/1 不允许VLAN1通过 C、如果要把 GigabitEthernet0/0/1 变为 Access 端口,首先 需要使用命令“un…

java.lang.UnsupportedOperationException: null 其一解决办法

文章目录 前言一、错误回顾1.详细信息2.代码详情 二、解决方案1.错误原因2.解决方案1.使用 new ObjectMapper() new TypeReference<List>(){}2.使用 SerializerFeature.WriteMapNullValue.getMask() 总结 前言 当我们远程调用传递泛型集合&#xff0c;如 List<?>…

基于springboot+vue的母婴商城系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目背景…

社会组织进村,温暖留守老人

2024年1月16日上午&#xff0c;巢湖市黄麓镇小李村迎来了30多位客人。他们是合肥格局商学院73队学员、部分书画家等爱心人士。大家深入村中和留守老人交流&#xff0c;给村民们现场书写春联&#xff0c;帮老人们照相&#xff0c;还带去食材做饭&#xff0c;陪伴老人们共同进餐。…

第11章 GUI Page500~504 步骤三十二:打开画板文件02

各个图元类新增GetTypeName_Static()&#xff0c;并将原来的GetTypeName()改为调用静态方法实现&#xff1a; 直线&#xff1a; 圆&#xff1a; 十字&#xff1a; 矩形&#xff1a; 文字&#xff1a; tool_4_save_load.hpp添加两行 tool_4_save_load.cpp增加&#xff1a; 增加…

原生微信小程AR序实现模型动画播放只播放一次,且停留在最后一秒

1.效果展示 0868d9b9f56517a9a07dfc180cddecb2 2.微信小程序AR是2023年初发布&#xff0c;还有很多问提&#xff08;比如glb模型不能直接播放最后一帧&#xff1b;AR识别不了金属、玻璃材质的模型等…有问题解决了的小伙伴记得告诉我一声&#xff09; 微信官方文档地址 3.代码…

【分享】MathWorks中国汽车年会:“软件定义汽车”

从软件赋能到软件定义&#xff0c;汽车行业不仅需要解决诸如错误发现滞后带来的高昂代价、功能融合所需的跨学科知识、功能安全与实施成本之间的权衡等老问题&#xff0c;也面临着新的挑战&#xff1a;软件复杂度的不断提升、利用数据驱动创造价值、人工智能的引入和实现、数字…

YOLOv3:算法与论文详细解读

【yolov1&#xff1a;背景介绍与算法精讲】 【yolo9000&#xff1a;Better, Faster, Stronger的目标检测网络】 目录 一、YOLOv3概述二、创新与改进三、改进细节3.1 多尺度特征3.2 不同尺度先验框3.3 完整的网络结构3.3 Darknet-53主干网络3.4 残差网络3.4.1 恒等映射3.4.2 网络…

软件测试|SQLAlchemy query() 方法查询数据

简介 上一篇文章我们介绍了SQLAlchemy 的安装和基础使用&#xff0c;本文我们来详细介绍一下如何使用SQLAlchemy的query()方法来高效的查询我们的数据。 创建模型 我们可以先创建一个可供我们查询的模型&#xff0c;也可以复用上一篇文章中我们创建的模型&#xff0c;代码如…

根据基因名批量查找它的Uniprot编号

背景&#xff1a; 前几天老师交给我一个任务&#xff0c;给我一个基因列表&#xff0c;让我查找它们所编码的蛋白质的蛋白质序列。我上了一下uniprot数据库&#xff0c;发现这个任务可以分成两步&#xff1a; 找到这个基因在Uniprot数据库中所对应的蛋白质编码根据蛋白质编码…

目标检测难题 | 小目标检测策略汇总

大家好&#xff0c;在计算机视觉中&#xff0c;检测小目标是最有挑战的问题之一&#xff0c;本文给出了一些有效的策略。 从无人机上看到的小目标 为了提高模型在小目标上的性能&#xff0c;本文推荐以下技术&#xff1a; 提高图像采集的分辨率 增加模型的输入分辨率 tile你…