C# 特性(Attribute)

news/2024/4/28 22:08:53/文章来源:https://blog.csdn.net/qq_42335551/article/details/130268405

一、特性(Attribute)定义

      特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。
      特性使用中括号声明,特性是一个类且必须直接或者间接继承 Attribute。 一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。

     

   1.创建一个自定义特性

     代码如下(示例):

   public class CustomAttribute : Attribute { }

   2.使用

代码如下(示例):

[Custom]public class Student {}

1.自定义的Attribute必须直接或者间接继承System.Attribute。

2.有一个约定:所有自定义的特性名称都应该有个Attribute后缀。因为当你的Attribute施加到一个程序的元素上的时候,编译器先查找你的Attribute的定义,如果没有找到,那么它就会查找“Attribute名称"+Attribute的定义。

二、Attribute关联的元素

 程序集(assembly)、模块(module)、类型(type)、属性(property)、事件(event)、字段(field)、方法(method)、参数(param)、返回值(return)。

1.先创建一个 CustomAttribute 自定义特性

代码如下(示例):

public class CustomAttribute : Attribute {public string Desc { get; set; }public CustomAttribute(){Console.WriteLine("CustomAttribute构造函数");}public CustomAttribute(string desc) { this.Desc = desc;Console.WriteLine("CustomAttribute有参构造函数");}}

 2.创建Student 使用特性

代码如下(示例):

 [Custom("我在类上使用")]public class Student {[Custom("我在字段上使用")]public string name;[Custom("我在属性上使用")]public string Name { get { return name; } set { name = value; } }[Custom("我在方法上使用")][return: Custom("我在返回值上")]public string GetName([Custom("参数")] int Id) {return name;}}

  2.如何使用

代码如下(示例):

  internal class Program{static void Main(string[] args){Student student=new Student() { Name="小明"};Console.WriteLine(student.GetName(1));Console.ReadKey();}}

调试结果


 从调试结果来看 跟不加特性没什么区别,那怎么体现出用到的特性呢

static void Main(string[] args){//Student student=new Student() { Name="小明"};//Console.WriteLine(student.GetName(1));Type type = typeof(Student);//判断是否在类上使用特性if (type.IsDefined(typeof(CustomAttribute), true)){CustomAttribute customAttribute = (CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute), true);Console.WriteLine(customAttribute.Desc);}MethodInfo method = type.GetMethod("GetName");//判断是否在方法上使用特性if (method.IsDefined(typeof(CustomAttribute), true)){CustomAttribute customAttribute = (CustomAttribute)method.GetCustomAttribute(typeof(CustomAttribute), true);Console.WriteLine(customAttribute.Desc);}ParameterInfo parameter = method.GetParameters()[0];//判断是否在参数上使用特性if (parameter.IsDefined(typeof(CustomAttribute), true)){CustomAttribute customAttribute = (CustomAttribute)parameter.GetCustomAttributes(typeof(CustomAttribute), true)[0];Console.WriteLine(customAttribute.Desc);}ParameterInfo returnParameter = method.ReturnParameter;//判断是否在方法的返回值上使用特性if (returnParameter.IsDefined(typeof(CustomAttribute), true)){CustomAttribute customAttribute = (CustomAttribute)returnParameter.GetCustomAttribute(typeof(CustomAttribute), true);Console.WriteLine(customAttribute.Desc);}PropertyInfo property = type.GetProperty("Name");//判断是否在属性上使用特性if (property.IsDefined(typeof(CustomAttribute), true)){CustomAttribute customAttribute = (CustomAttribute)property.GetCustomAttribute(typeof(CustomAttribute), true);Console.WriteLine(customAttribute.Desc);}FieldInfo field = type.GetField("name");//判断是否在字段上使用特性if (field.IsDefined(typeof(CustomAttribute), true)){CustomAttribute customAttribute = (CustomAttribute)field.GetCustomAttribute(typeof(CustomAttribute), true);Console.WriteLine(customAttribute.Desc);}Console.ReadKey();}

调试结果

可以发现 ,并没有对Student类进行初始化,但是我们在Student使用到特性的地方,取到的Attribute信息

  3.总结

   Attribute类是在编译的时候被实例化的,而不是像通常的类那样在运行时候才实例化。

三、Attribute的运用

 使用场景:某些时候我们在程序处理过程中为了程序更加健全及安全需要校验一些参数的合法性,比如邮件格式校验、用户的年龄等类似的需求,以下以邮件合法及用户年龄的范围只能在1~100范围为例。刚开始我们最直接也最先想到的就是传统的方式写参数校验。如下所示:

 internal class Program{static void Main(string[] args){User user = new User();if (string.IsNullOrWhiteSpace(user.Email))  //:这里只判断了邮箱为空,省略了邮箱合法性判断{Console.WriteLine("Email 参数不符合!");//:这里不执行保存,提示用户参数不合法}if (user.Age < 100 || user.Age > 0) {Console.WriteLine("Age 参数不符合,Age范围只能是0~100");//:这里不执行保存,提示用户参数不合法}Console.ReadKey();}}public class User{      public string Name { get; set; }public int Age { get; set; }public string Email { get; set; }}

这种会造成代码重复,且维护困难

第二种:由于第一种产生的问题,有的小伙伴会将验证逻辑放到 get set 访问器中,这种造成了职责不分明,实体本身是承载信息的,不需要存在业务逻辑

  public class User{      public string Name { get; set; }private int age;public int Age { get {             return this.age;   } set { if (value >=0 && value <=100){this.age = value;}else{throw new Exception("Age不合法");}} }public string Email { get; set; }}

特性方式:由于以上两种产生的问题,我们可以使用特性进行处理,易于维护、易于编码、易于公用

1.定义一个抽象类,继承自Attribute

    public abstract class AbstractValidateAttribute : Attribute{public abstract bool Validate(object oValue);}

 2.定义一个 LongAttribute

继承 AbstractValidateAttribute,其中有个最小、最大字段, 重写Validate 方法用于处理效验逻辑

   public class LongAttribute : AbstractValidateAttribute{private int min { get; set; }private int max { get; set; }public LongAttribute(int min, int max){this.min = min;this.max = max;}public override bool Validate(object oValue){return oValue != null && int.TryParse(oValue.ToString(), out int num) && num >= this.min && num <= this.max;}}

 3.定义一个用户表 User ,标上LongAttribute特性

继承 AbstractValidateAttribute,其中有个最小、最大字段, 重写Validate 方法用于处理效验逻辑

 public class User{public string Name { get; set; }[Long(1, 100)]public int Age{get; set;}}

 4.定义一个 Validate 的扩张类与扩张方法

  其作用是取得对象 Type 获取 LongAttribute 并调用其 Validate 进行业务逻辑效验

  public static class ValidateExtension{public static bool Validate(this object val){Type type = val.GetType();foreach (var prop in type.GetProperties()){if (prop.IsDefined(typeof(LongAttribute), true)){LongAttribute longAttribute = (LongAttribute)prop.GetCustomAttribute(typeof(LongAttribute), true);if (!longAttribute.Validate(prop.GetValue(val))){return false;}}}return true;}}

5.实例化 User

  分别实例化两个 User,并对Age字段赋予不合法与合法的值

  static void Main(string[] args){User user1 = new User();user1.Age = -1;User user2 = new User();user2.Age = 23;Console.WriteLine(user1.Validate());Console.ReadKey();}

6.总结

 后期如果新增不同参数类型的校验,只需要新增对应的类,继承自AbstractValidateAttribute ,在新增的类中实现具体的校验逻辑,并且在需要校验的属性上标记对应的特性即可,方便代码扩展

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

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

相关文章

2023年制造业产品经理考NPDP有什么用?

产品经理国际资格认证NPDP是新产品开发方面的认证&#xff0c;集理论、方法与实践为一体的全方位的知识体系&#xff0c;为公司组织层级进行规划、决策、执行提供良好的方法体系支撑。 【认证机构】 产品开发与管理协会&#xff08;PDMA&#xff09;成立于1979年&#xff0c;是…

键盘录入及标识符

键盘录入 键盘录入介绍&#xff1a; ●为什么要有键盘录入? 目的&#xff1a;为了让我们操作的数据,变得更加灵活 举例&#xff1a;int a10; 这里a虽然是个变量&#xff0c;但记录的值&#xff0c;却是手动写死的。 提问&#xff1a;能不能让a变量记录的值&#xff0c;灵活…

electron编译环境搭建和第一个桌面应用例子

前言 Electron是基于Chromium和Node.js实现的&#xff0c;所以开发人员所需要使用到的前端技术主要包括以下方面&#xff1a; 1、Html、CSS、JavaScript、ES6 2、前端开发工具Vue、Angular、React等的一种 3、其他网络、缓存、通讯、系统、跟踪等前端技术 4、对Vscode编辑…

JUC高级十二-ReentrantLock、ReentrantReadWriteLock、StampedLock

无锁→独占锁→读写锁→邮戳锁 1. 关于锁的大厂面试题 你知道Java里面有哪些锁?你说你用过读写锁&#xff0c;锁饥饿问题是什么&#xff1f;有没有比读写锁更快的锁&#xff1f;StampedLock知道吗?(邮戳锁/票据锁)ReentrantReadWriteLock有锁降级机制策略你知道吗&#xff1…

总结827

学习目标&#xff1a; 4月&#xff08;复习完高数18讲内容&#xff0c;背诵21篇短文&#xff0c;熟词僻义300词基础词&#xff09; 学习内容&#xff1a; 高等数学&#xff1a;刷1800&#xff0c;做了26道计算题&#xff0c;记录两道错题&#xff0c;搞懂了&#xff0c;但并不…

家庭智能开关通断—Homekit智能

智能通断器&#xff0c;也叫开关模块&#xff0c;可以非常方便地接入家中原有开关、插座、灯具、电器的线路中&#xff0c;通过手机App或者语音即可控制电路通断&#xff0c;轻松实现原有家居设备的智能化改造。 随着智能家居概念的普及&#xff0c;越来越多的人想将自己的家改…

HuggingFace入门教程--环境搭建

HuggingFace中文直译为”拥抱脸“&#xff0c;是最近非常火爆的一个人工智能社区&#xff0c;官网地址是&#xff1a;https://huggingface.co/ .关于HuggingFace的相关介绍大家可以自行百度。本文主要为刚入人工智能坑的小白指下路&#xff0c;同时也是逼着自己记录下学习过程中…

【ctfshow】命令执行->web29-web44

前言 半夜网抑云听歌听emo了 z 刷会儿题不然睡不着了呜呜呜 红中(hong_zh0) CSDN内容合伙人、2023年新星计划web安全方向导师、 华为MindSpore截至目前最年轻的优秀开发者、IK&N战队队长、 吉林师范大学网安大一的一名普通学生、搞网安论文拿了回大挑校二、 阿里云专家博…

线性链表 反转 -(递归与非递归算法)_20230420

线性链表 反转 -(递归与非递归算法)_20230420 前言 线性链表反转是非常有趣的算法&#xff0c;它可以采用多种方式实现&#xff0c;比较简洁的方法是递归反转&#xff1b;传统的方式是利用迭代反转&#xff0c;设定三个变量&#xff0c;采用类似滚动数组的方式&#xff0c;实…

qt中使用 ui 文件进行界面设计

目录 1、创建 Qt 应用 ​2、项目创建成功 3、直接点击打开 mainwindow.ui 文件 4、随便从左边侧边栏拖拽一个空间到 界面设计区域 5、在右侧边栏右键点击 pushButton 控件&#xff0c;点击转到槽 6、根据实际需要选择对应的信号&#xff0c;我这里方便演示选择 clicked&a…

SpringCloud --- Ribbon负载均衡

一、负载均衡原理 SpringCloud底层其实是利用了一个名为Ribbon的组件&#xff0c;来实现负载均衡功能的。 那么我们发出的请求明明是http://userservice/user/1&#xff0c;怎么变成了http://localhost:8081的呢&#xff1f; 二、源码跟踪 为什么我们只输入了service名称就…

【博学谷学习记录】超强总结,用心分享 | 架构师 MinIO学习总结

文章目录 MinIO对象存储的概念计算机数据存储系统-架构模式对象存储的优势常见的对象存储系统/服务&#xff08;Object Storage Service&#xff0c;OSS&#xff09; MinIO简介特点高级特性小结 MinIO部署基于 linux Binary 部署 MinIO ServerMinIO数据组织结构MinIO Client**基…

ISO-27145故障诊断说明

ISO-27145故障诊断说明 2.1 27145目录说明 ISO27145-1: 这里边介绍的是一般信息和用例定义&#xff1b; ISO27145-2: 这里边介绍的是与排放相关的通用数据规则&#xff0c;用于查询&#xff1b; ISO27145-3: 这里边主要介绍了支持的服务 12服务 14服务 19服务 22服务 31服务&…

「C/C++」C/C++强制类型转换

博客主页&#xff1a;何曾参静谧的博客 文章专栏&#xff1a;「C/C」C/C学习 目录 相关术语C语言中的强制类型转换C中的强制类型转换static_castdynamic_castreinterpret_castconst_cast 注意事项 相关术语 强制类型转换&#xff1a;是指将一个数据类型强制转换为另一个数据类型…

Python初学小知识(十四):数据分析处理库Pandas

Python初学小知识&#xff08;十四&#xff09;&#xff1a;数据分析处理库Pandas 十八 Pandas1 文件读取1.1 读取csv1.2 读取txt1.3 读取excel&#xff08;xlsx&#xff09; 2 内容读取2.1 读取行2.2 读取列 3 数据处理3.1 加减乘除3.1.1 列 与 元素3.1.2 列 与 列 3.2 最值、…

GoJS Beginner Tutorial #1

1.关系图&#xff1a; gojs部件由一个或多个gojs面板组成&#xff0c;这些面板包含和组织各种gojs图形对象 通常使用go.GraphObject.make创建一个GraphObject&#xff0c;我们通过使用$符号变量缩短了该函数的名称 这个函数的第一个参数&#xff0c;往往是你想要制作的GraphOb…

Centos切换jdk版本

先安装了jdk1.8的版本&#xff0c;需要使用jdk17的版本 1.先安装jdk17&#xff0c;再配置环境变量&#xff1a; vim ~/.bashrc 2.在最后一行添加 ##这个添加的就是路径&#xff0c;一定要和自己jdk安装的路径是一致的 export JAVA_HOME/usr/lib/jvm/java-8-openjdk-amd64 3.然…

docker容器:docker镜像的三种创建方法及dockerfile案例

目录 一、基于现有镜像创建 1、创建启动镜像 2、生成新镜像 二、基于本地模板创建 1、OPENVZ 下载模板 2、导入容器生成镜像 三、基于dockerfile创建 1、dockerfile结构及分层 2、联合文件系统 3、docker镜像加载原理 4、dockerfile操作常用的指令 (1)FROM指令 (…

响应式布局

文章目录 响应式布局概述viewport 视口CSS 常用单位CSS 媒体查询语法直接使用使用style标签使用link引入 自适应布局栅格系统响应式布局案例rem媒体查询 响应式布局 概述 响应式布局是指网站或应用程序可以自适应不同的屏幕尺寸和设备类型&#xff0c;简而言之就是一个网站兼…

Sentinel同时配置fallback和blockHandler的问题

Spring Cloud在使用Sentinel进行服务降级和熔断时&#xff0c;如果同时配置了fallback和blockHandler&#xff0c;则在服务熔断后&#xff0c;抛出的BlockException不会再fallback逻辑中执行&#xff0c;而是在blockHandler逻辑中执行。 首先来看只配置了fallback的情况&#x…