C#入门(13):特性Attribute

C# 特性(Attributes)是用于在运行时为程序元素(如类、方法、属性等)添加声明性信息的一种方式。这些信息可以在程序运行时通过反射(Reflection)访问。特性可以用来控制程序行为、添加元数据或者影响程序的运行时行为。

.Net 框架提供了两种类型的特性:预定义特性和自定义特性。
在这里插入图片描述
用法如下:

#define DEBUG_PRINTusing System;
using System.Diagnostics;class Test
{// 如果定义的 DEBUG_PRINT ,该函数可以执行;没有定义DEBUG_PRINT,该函数将无法执行[Conditional("DEBUG_PRINT")]static void function1(){Console.WriteLine("DEBUG_PRINT In Function 1.");}[Obsolete("Don't use OldMethod, use NewMethod instead", true)]public static void OldMethod(){Console.WriteLine("It is the old method");}public static void NewMethod(){Console.WriteLine("It is the new method");}public static void Main(){Console.WriteLine("Main");//OldMethod();  提示,Don't use OldMethod, use NewMethod insteadfunction1();}
}

Conditional

Conditional类似于条件编译,在上面的程序中当预定义DEBUG_PRINT,那么运行时就会调用function1,预定义放到了代码文件的最上面:

#define DEBUG_PRINT

Obsolete

Obsolete表示舍弃,可用于警告提示。

自定义属性

MyCustomAttribute.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Attribute02
{[AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct|System.AttributeTargets.Method)]internal class MyCustomAttribute : System.Attribute{private string description;private string returnType;// 在构造函数中指定属性信息public MyCustomAttribute(string desc, string returnType){this.description = desc;this.returnType = returnType;}public string GetDescription(){return this.description;}public string GetReturnType(){return this.returnType;}}
}

使用MyCustomAttribute修饰类MyClass和它的成员方法GetSum

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Attribute02
{[MyCustomAttribute("This is a custom description", "Class No return")]internal class MyClass{[MyCustomAttribute("This is a custom description", "Method int return")]public int GetSum(int x, int y){return x + y;}}
}

Main测试

namespace Attribute02
{internal class Program{static void Main(string[] args){Console.WriteLine("测试自定义属性");// 反射出类的属性信息Type type = typeof(MyClass);object[] attributes = type.GetCustomAttributes(typeof(MyCustomAttribute), true);foreach (MyCustomAttribute attr in attributes){Console.WriteLine(attr.GetDescription());Console.WriteLine(attr.GetReturnType());}// 反射出方法的属性信息var method = typeof(MyClass).GetMethod("GetSum");var attributesMethon = method.GetCustomAttributes(typeof(MyCustomAttribute), false);if (attributesMethon.Length > 0){var myAttribute = (MyCustomAttribute)attributesMethon[0];Console.WriteLine(myAttribute.GetDescription());Console.WriteLine(myAttribute.GetReturnType());}}}
}

运行结果如下:
测试自定义属性
This is a custom description
Class No return
This is a custom description
Method int return

自定义特性应用多个属性

在C#中,AttributeUsage 属性用来定义一个自定义属性可以应用到的程序元素,以及该属性是否可以多次应用和是否可以被继承。
validOn它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。用以指定特性可以应用到哪些程序元素(如类、结构、方法等)上。

[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property, 
AllowMultiple = true)]

AllowMultiple 参数是一个布尔值,指示是否可以对单一程序元素应用多个此类特性实例。
Inherited 参数也是一个布尔值,指示特性是否可以被派生类继承。
例如

namespace Attribute03
{// 定义一个自定义特性,可以应用于类和结构,可以被继承,并且允许多次应用[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = true, AllowMultiple = true)]public class MyCustomAttribute : Attribute{public string Name { get; set; }public MyCustomAttribute(string name){Name = name;}}
}

使用MyCustomAttribute修饰ExampleClass

namespace Attribute03
{// 应用自定义特性到一个类多次[MyCustomAttribute("ExampleClass")][MyCustomAttribute("AnotherNameForTheSameClass")]public class ExampleClass{// 类的实现内容}
}

Main测试

namespace Attribute03
{internal class Program{static void Main(string[] args){Console.WriteLine("Attribute03");// 通过反射获取特性信息var attributes = typeof(ExampleClass).GetCustomAttributes(typeof(MyCustomAttribute), true);foreach (MyCustomAttribute attr in attributes){Console.WriteLine($"Class {typeof(ExampleClass).Name} has the custom attribute with name: {attr.Name}");}}}
}

其它用法

C# 特性Attribute除了用于添加元数据和通过反射检索信息之外,它们还可用于以下目的:

  1. 控制程序行为

    • 编译器指令:特性可以用来给编译器提供指令,如 [Obsolete] 用于标记过时的代码元素。
    • 条件编译:特性可用于条件编译,例如 [Conditional("DEBUG")] 可以使方法仅在 DEBUG 模式下编译和执行。
  2. 数据验证

    • 在数据模型中,特性经常用于验证数据。例如,在实体框架(Entity Framework)或数据注释(Data Annotations)中,你可以使用 [Required], [StringLength] 等特性来定义数据验证规则。
  3. 序列化和反序列化控制

    • 在数据序列化过程中,特性用于控制如何将对象转换为 XML 或 JSON 等格式。例如,[Serializable][DataContract][DataMember]
  4. 拦截器和动态代理

    • 在面向切面编程(AOP)中,特性用于定义方法拦截器。这在动态代理创建时特别有用,例如在 .NET Core 中的依赖注入(DI)。
  5. 声明性安全

    • 特性可用于定义安全要求,如 [PrincipalPermission] 用于声明方法执行所需的安全上下文。
  6. 编写插件和扩展

    • 在插件架构中,特性可用于标识插件类或方法,便于动态加载和识别。
  7. 单元测试框架

    • 在单元测试中,特性用于标记测试方法和测试类(例如 [TestMethod][TestClass]),以及进行测试设置和清理(如 [TestInitialize][TestCleanup])。
  8. 依赖注入配置

    • 在依赖注入(DI)中,特性可以用于标记构造函数、属性或方法,以指导 DI 容器如何进行注入。
  9. 框架和库集成

    • 许多框架和库使用特性来集成与特定框架或库的功能,如 ASP.NET Core 中的路由、授权和过滤器特性(如 [Route], [Authorize], [ActionFilter] 等)。

通过这些用途,C# 特性成为了一种强大的机制,可以在不改变代码本身逻辑的情况下丰富和扩展代码的功能。

代码如下:

1. 编译器指令([Obsolete] 特性)

public class MyClass
{[Obsolete("Use NewMethod instead", false)]  // 标记为过时public void OldMethod(){Console.WriteLine("This is the old method.");}public void NewMethod(){Console.WriteLine("This is the new method.");}
}

2. 数据验证(使用数据注释)

using System.ComponentModel.DataAnnotations;public class User
{[Required]public string Name { get; set; }[StringLength(10, ErrorMessage = "ID cannot be longer than 10 characters.")]public string ID { get; set; }
}

3. 序列化和反序列化控制([Serializable][DataMember] 特性)

using System.Runtime.Serialization;[Serializable]
public class Person
{public string Name { get; set; }[DataMember]public int Age { get; set; }
}

4. 面向切面编程(AOP)中的方法拦截器

public class LoggingAttribute : Attribute
{// 这里只是示例,实际的拦截实现需要结合拦截器框架使用public void BeforeCall() => Console.WriteLine("Before method call");public void AfterCall() => Console.WriteLine("After method call");
}public class MyClass
{[Logging]public void MyMethod(){Console.WriteLine("Executing the method.");}
}

5. 声明性安全

using System.Security.Permissions;public class SecureClass
{[PrincipalPermission(SecurityAction.Demand, Role = "Administrator")]public void SecureMethod(){Console.WriteLine("This method requires the Administrator role.");}
}

6. 插件和扩展标识

public class PluginAttribute : Attribute
{public string Name { get; set; }
}[Plugin(Name = "MyPlugin")]
public class MyPlugin
{// 插件的实现
}

7. 单元测试

using Microsoft.VisualStudio.TestTools.UnitTesting;[TestClass]
public class MyTestClass
{[TestMethod]public void MyTestMethod(){// 测试代码}
}

8. 依赖注入配置

public class MyService
{[Inject]public IDependency MyDependency { get; set; }// 假设 Inject 是一个标记依赖注入的特性
}

9. 框架和库集成

using Microsoft.AspNetCore.Mvc;public class MyController : Controller
{[Route("api/myroute")]public IActionResult MyAction(){return Ok();}
}

以上代码示例涵盖了特性在 C# 中的各种不同用途,展示了特性如何被应用于实际编程场景中。

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

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

相关文章

SpringBoot趣探究--1.logo是如何打印出来的

一.前言 从本篇开始,我将对springboot框架做一个有趣的探究,探究一下它的流程,虽然源码看不懂,不过我们可以一点一点慢慢深挖,好了,下面我们来看一下本篇的知识,这个logo是如何打印出来的&#…

数字化转型导师坚鹏:数字化时代银行网点厅堂营销5大特点分析

数字化时代银行网点厅堂营销存在以下5大特点: 1、产品多样化:在数字化时代,银行的产品和服务变得更加多样化。除了传统的存款、贷款、理财等金融服务外,还新增了各种创新产品,如网上银行、移动支付、投资咨询、保险、…

【开源】基于微信小程序的音乐平台

项目编号: S 055 ,文末获取源码。 \color{red}{项目编号:S055,文末获取源码。} 项目编号:S055,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示 四、核心代码4.1 查询单首…

开源的进销存系统都有哪些?

开源的进销存系统有很多,以下是其中一些比较流行的: OpenERP:一个集成了多个业务功能的开源ERP软件,可以实现进销存管理,会计,仓库管理,销售管理等业务功能。 Odoo:是OpenERP的一个分支&#x…

C语言进阶之冒泡排序

✨ 猪巴戒:个人主页✨ 所属专栏:《C语言进阶》 🎈跟着猪巴戒,一起学习C语言🎈 目录 前情回顾 1、回调函数 2、冒泡排序 3、库函数qsort cmp(sqort中的比较函数,需要我们自定义) …

STM32F4串口USART发送为00的解决方案

检查接线是否正确检查TX是否为复用推挽输出 3.检查是否将TX和RX引脚重映射为USART功能 在STM32中,每个GPIO引脚可以配置为不同的复用功能,例如UART、SPI、I2C等。具体来说,GPIO_PinAFConfig函数用于配置GPIO引脚的复用功能。它的参数包括GPIO…

2023年【四川省安全员A证】复审考试及四川省安全员A证考试试题

题库来源:安全生产模拟考试一点通公众号小程序 四川省安全员A证复审考试根据新四川省安全员A证考试大纲要求,安全生产模拟考试一点通将四川省安全员A证模拟考试试题进行汇编,组成一套四川省安全员A证全真模拟考试试题,学员可通过…

c++|引用

目录 一、引用概念 二、引用特性 三、常引用 (具有常属性的引用变量) 四、使用场景 一、引用概念 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,他和他引用的变量共用同…

Spring Cloud 简介

1、简介 Spring CloudLevel up your Java code and explore what Spring can do for you.https://spring.io/projects/spring-cloud Spring Cloud 是一系列有序框架的集合,其主要的设施有,服务发现与注册,配置中心,消息总…

计算机组成原理-主存储器与CPU的连接

文章目录 知识总览单块存储芯片与CPU的连接位扩展(存储字的位数)字扩展(存储字数)关于线选法和片选法字位同时扩展总结补充:译码器 知识总览 单块存储芯片与CPU的连接 数据总线,地址总线,片选线…

Postman插件如何安装(一)

我们chrome插件网热门推荐的软件之一就是postman。但是postman的适应平台分为:postman chrome应用程序,postman应用程序,postman插件。谷歌应用商店从2018年3月开始停止chrome应用程序的更新。除非继续使用老版本的postman chrome应用程序&am…

【代码随想录】刷题笔记Day33

前言 Day33虽说是一个月,但是从第一篇开始实际上已经过了8个月了,得抓紧啊 46. 全排列 - 力扣(LeetCode) 前面组合就强调过差别了,这道题是排序,因此每次要从头到尾扫,结合used数组 class So…

数字IC基础:有符号数和无符号数的加减运算

相关阅读 数字IC基础https://blog.csdn.net/weixin_45791458/category_12365795.html?spm1001.2014.3001.5482 首先说明,本篇文章并不涉及补码运算正确性的证明,仅是对补码运算在有符号数和无符号数中运行进行讨论。 补码运算最大的作用在于消除计算机…

机器学习8:在病马数据集上进行算法比较(ROC曲线与AUC)

ROC曲线与AUC。使用不同的迭代次数(基模型数量)进行 Adaboost 模型训练,并记录每个模型的真阳性率和假阳性率,并绘制每个模型对应的 ROC 曲线,比较模型性能,输出 AUC 值最高的模型的迭代次数和 ROC 曲线。 …

5 个适用于 Linux 的开源日志监控和管理工具

当Linux等操作系统运行时,会发生许多事件和在后台运行的进程,以实现系统资源的高效可靠的使用。这些事件可能发生在系统软件中,例如 init 或 systemd 进程或用户应用程序,例如 Apache、MySQL、FTP 等。 为了了解系统和不同应用程序…

UE5和UE4版本更新重大改变汇总。

转载:UE5和UE4版本更新重大改变汇总。 - 知乎 (zhihu.com) 用户界面变化: 1,原先拖动给放置Actor的place actors,世界大纲,Level等都可以通过右击隐藏到侧边栏; 2,Command命令窗口和ContentBr…

优秀智慧园区案例 - 佛山美的工业城零碳智慧园区,先进智慧园区建设方案经验

一、项目背景 美的工业园区西区最早建于上世纪90年代,到现在已经过去近30年,而这三十年恰恰是信息科技大发展的30年,原有的生产办公条件已不能很好的承载新时期办公和参观接待的需求。所以在21年美的楼宇科技事业部决定对原来的园区进行改造…

文本转语音

免费工具 音视频转译 通义听悟 | https://tingwu.aliyun.com/u/wg57n33kml5nkr3p 音色迁移 speechify | https://speechify.com/voice-cloning/ 视频生成 lalamu | http://lalamu.studio/demo/ 画质增强 topazlabs video AI | https://www.topazlabs.com 付费工具 rask | htt…

LeetCode热题100——动态规划

动态规划 1. 爬楼梯2. 杨辉三角3. 打家劫舍 1. 爬楼梯 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? // 题解:每次都有两种选择,1或者2 int climbStairs(int n) {if (n …

STM32CubeMX学习笔记-CAN接口使用

STM32CubeMX学习笔记-CAN接口使用 CAN总线传输协议1.CAN 总线传输特点2.位时序和波特率3.帧的种类4.标准格式数据帧和遥控帧从STM32F407参考手册中可以看出主要特性如下CAN模块基本控制函数CAN模块消息发送CAN模块消息接收标识符筛选发送中断的事件源和回调函数 CubeMX项目设置…