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.mfbz.cn/a/319753.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springboot怎样设置全局的traceId(包括MQ)

一、Controller打印TraceId 1、拦截所有的controller,输入输出将traceId放入MDC中: package com.perkins.ebicycle.mobile.trace;import java.util.Arrays; import java.util.List; import java.util.UUID; import java.util.stream.Collectors;import…

深思熟虑可能性模型介绍与使用

深思熟虑可能性模型介绍与使用 如何联系我 作者:鲁伟林 邮箱:thinking_fioa163.com或vlinyes163.com 版权声明:文章和记录为个人所有,如果转载或个人学习,需注明出处,不得用于商业盈利行为。 背景 20…

操作系统详解(5.1)——信号(Signal)的相关题目

系列文章: 操作系统详解(1)——操作系统的作用 操作系统详解(2)——异常处理(Exception) 操作系统详解(3)——进程、并发和并行 操作系统详解(4)——进程控制(fork, waitpid, sleep, execve) 操作系统详解(5)——信号(Signal) 文章目录 题目第一问第二问第三问 题目…

ES搜索的安装以及常用的增删改查操作(已经写好json文件,可以直接使用)

1.es的下载 https://www.elastic.co/cn/downloads/past-releases 2.elasticsearch安装及配置,遇到9200访问不了以及中文乱码,能访问了却要账户密码等问题 Elasticsearch启动后访问9200失败_http://localhost:9200无返回值-CSDN博客 3.开启es服务&#x…

Qat++,轻量级开源C++ Web框架

目录 一.简介 二.编译Oat 1.环境 2.编译/安装 三.试用 1.创建一个 CMake 项目 2.自定义客户端请求响应 3.将请求Router到服务器 4.用浏览器验证 一.简介 Oat是一个面向C的现代Web框架 官网地址:https://oatpp.io github地址:https://github.co…

Error: L6218E: Undefined symbol 系列错误汇总 (referred from main.o)

传送门 错误1: Undefined symbol(referred from main.o)错误2:Undefined_symbol _use_two_region memory 错误1: Undefined symbol(referred from main.o) Cube_GPIO\Cube_GPIO.axf: Error: L6218E: Undefined symbol LED_GPIO_Init (referr…

15个为你的品牌增加曝光的维基百科推广方法-华媒舍

维基百科是全球最大的免费在线百科全书,拥有庞大的用户群体和高质量的内容。在如今竞争激烈的市场中,利用维基百科推广品牌和增加曝光度已成为许多企业的重要策略。本文将介绍15种方法,帮助你有效地利用维基百科推广品牌,提升曝光…

GPT编程:运行第一个聊天程序

环境搭建 很多机器学习框架和类库都是使用Python编写的,OpenAI提供的很多例子也是Python编写的,所以为了方便学习,我们这个教程也使用Python。 Python环境搭建 Python环境搭建有很多种方法,我们这里需要使用 Python 3.10 的环境…

浅谈对Mybatis的理解

一、Mybatis的概述 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,由谷歌托管,并且改名为MyBatis 。2013年11月迁移到Github。 MyBatis是支持普通SQL查询,存储过程和高级映射的优…

ssm基于Web的课堂管理系统设计与实现论文

目 录 目 录 I 摘 要 III ABSTRACT IV 1 绪论 1 1.1 课题背景 1 1.2 研究现状 1 1.3 研究内容 2 2 系统开发环境 3 2.1 vue技术 3 2.2 JAVA技术 3 2.3 MYSQL数据库 3 2.4 B/S结构 4 2.5 SSM框架技术 4 3 系统分析 5 3.1 可行性分析 5 3.1.1 技术可行性 5 3.1.2 操作可行性 5 3…

数据库(1)

目录 1.什么是数据库 1.1.数据 1.2.数据库 1.3 常见数据库 1.3.1 关系型数据库 1.3.2 非关系型数据库 2.mysql概述 ​编辑 **3.MySQL本地仓库安装 在Linux端操作 4.MySQL网络安装 **方法一:RPM捆绑包 下载安装RPM捆绑包: 在windows端操作&a…

开箱即用的企业级前后端分离【.NET Core6.0 Api + Vue 2.x + RBAC】权限框架-Blog.Core

前言 今天要给大家推荐一个开箱即用的企业级前后端分离【.NET Core6.0 Api Vue 2.x RBAC】权限框架(提高生产效率,快速开发就选它):Blog.Core。 推荐原因 Blog.Core通过详细的文章和视频讲解,将知识点各个击破&…

element表格数据,表头上(下)角标,html字符串渲染

1. 问题描述 在动态渲染的element表格中&#xff0c;表头和表中数据是一个含有html的字符串&#xff0c;需要渲染 2. 效果 3. 代码 const columns ref([{ text: 差值<sub>-3</sub> / 10<sup>-6</sup>℃<sup>-1</sup>, value: aallowEr…

三菱FX系列PLC定长切割控制(线缆裁切)

三菱PLC绝对定位指令DDRVA实现往复运动控制详细介绍请查看下面文章链接&#xff1a; https://rxxw-control.blog.csdn.net/article/details/135570157https://rxxw-control.blog.csdn.net/article/details/135570157这篇博客我们介绍线缆行业的定长切割控制相关算法。 未完待…

Xmind 网页端登录及多端同步

好久没用 Xmind 了&#xff0c;前几天登录网页端突然发现没办法登录了&#xff0c;总是跳转到 Xmind AI 页面。本以为他们不再支持网页端了&#xff0c;后来看提示才知道只是迁移到了新的网址&#xff0c;由原来的 xmind.works 现在改成了的 xmind.ai。又花费好长时间才重新登录…

Vue3 移动端自适应方案postcss-px-to-viewport

我的环境 依赖名版本pnpm8.14.0Node16.20.1Vue3.3Vite5.0.8 一、安装 pnpm install postcss-px-to-viewport1.1.1 --save-dev 二、配置 vite.config.ts import postcsspxtoviewport from postcss-px-to-viewportexport default defineConfig({css: {postcss: {plugins: [p…

【HarmonyOS】网络数据请求连接与数据持久化操作

从今天开始&#xff0c;博主将开设一门新的专栏用来讲解市面上比较热门的技术 “鸿蒙开发”&#xff0c;对于刚接触这项技术的小伙伴在学习鸿蒙开发之前&#xff0c;有必要先了解一下鸿蒙&#xff0c;从你的角度来讲&#xff0c;你认为什么是鸿蒙呢&#xff1f;它出现的意义又是…

QT获取程序编译时间与当前时间的区别及应用场景

一.获取编译时间与当前时间的区别 1.编译日期时间&#xff1a;这个信息通常用于标识某个源代码文件或整个应用程序的编译时间&#xff0c;程序一旦编译出来不会再改变&#xff0c;通常用于记录或跟踪代码的版本和更改历史。 2.运行当前日期时间&#xff1a;这是指程序在运行时…

ssm基于web的电影购票系统+vue论文

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统电影购票信息管理难度大&#xff0c;容错率低&#xff0c…

NI PXIe-6386国产替代,8路AI(16位,14 MS/s/ch),2路A​O,24路DIO,PXI多功能I/O模块

PXIe-6386 PXIe&#xff0c;8路AI&#xff08;16位&#xff0c;14 MS/s/ch&#xff09;&#xff0c;2路A​O&#xff0c;24路DIO&#xff0c;PXI多功能I/O模块 PXIe-6386是一款同步采样的多功能DAQ设备。该模块提供了模拟 I/O、数字I/O、四个32位计数器和模拟和数字触发。板载N…
最新文章