C# 特性(Attribute)

news2024/11/15 8:12:29

一、特性(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.coloradmin.cn/o/453007.html

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

相关文章

ArduPilot之开源代码框架

ArduPilot之开源代码框架 1. 系统框架2. 工程框架2.1 工程目录2.2 代码组成2.3 运行流程 4. 硬件传感器总线4.1 I2C4.2 SPI4.3 UART4.4 CAN 5. 软件设计概念6. 总结7. 参考资料 在研读ArduPilot的过程&#xff0c;尝试用一些中文的词汇来描述&#xff0c;可能会造成某些理解上的…

sparksql select后插入自己 报错 Cannot overwrite a path that is also being read from

问题现象 spark.version < 3.0.1 执行下面语句报错: Cannot overwrite a path that is also being read from ... 哪些情况算同时读写自己? 如果读自己和写自己在一个spark stage中,就算同时读写自己. spark.table("tb1")// 其他stransform.write.mode("…

运维监控工具PIGOSS BSM扩展指标介绍

PIGOSS BSM运维监控工具&#xff0c;除系统自带指标外&#xff0c;还支持添加SNMP扩展指标、脚本扩展指标、JMX扩展指标、自定义JDBC指标等&#xff0c;今天本文将介绍如何添加SNMP扩展指标和脚本扩展指标。 添加SNMP扩展指标 前提&#xff1a;需要知道指标的oid 例子&#xff…

刚毕业在深圳做程序员,我来聊聊月薪1万在大城市生活能剩下多少

我今年刚毕业&#xff0c;然后在大城市做IT&#xff0c;可能工资大概1万左右。现在讨论一下&#xff0c;月薪1万在大城市最后能留下多少。以下是兴哥的一位刚毕业没多久的程序员朋友&#xff0c;给大家分享一下程序员的薪资水平&#xff0c;在大城市生活 &#xff0c;一个月会剩…

JDBC重点

JDBC初识 DriverManager 将第三方数据库厂商的实现驱动jar注册到程序中可以根据数据库连接信息获取connection Connection 和数据库建立的连接,在连接对象上,可以多次执行数据库curd动作 可以获取statement和 preparedstatement,callablestatement对象 Statement | Prepare…

网络基础,InetAddress,Socket,TCP,UDP

概念&#xff1a;两台设备之间通过网络实现数据运输网络通信&#xff1a;将数据通过网络从一台设备传输到另一台设备java.net包下提供了一系列的类或接口&#xff0c;供程序员使用&#xff0c;完成网络通信网络&#xff1a;两台或多台设备通过一定物理设备连接起来构成了网络根…

go1.20环境安装以及beego框架配置

打开网址下载安装包选择对应安装包来下载安装(个人是windows&#xff0c;下载的1.20.3版本) 默认情况下会安装在C盘&#xff0c;但是我安装在了D盘目录 根据安装提示一步步next&#xff0c;直至完成 go get 在1.18版本之后就弃掉了&#xff0c;换成了install 配置自己的work…

Spring Cloud Alibaba全家桶——微服务链路追踪SkyWalking

前言 本文小新为大家带来 微服务链路追踪SkyWalking 相关知识&#xff0c;具体内容包括SkyWalking简介&#xff0c;SkyWalking环境搭建部署&#xff0c;SkyWalking接入微服务&#xff0c;SkyWalking持久化跟踪数据&#xff0c;自定义SkyWalking链路追踪&#xff0c;SkyWalking集…

ARM相关重点

一、概念&#xff1a; 指令&#xff1a;就是一条汇编指令 指令集&#xff1a;很多条汇编指令的集合 架构&#xff1a;随着ARM产品的迭代升级&#xff0c;对ARM指令集的命名 armv1~armv6已经淘汰 armv7~armv8市面正在使用的 armv9:2021年刚上市 内核&#xff1a;根据不同的a…

SpringCloud分布式请求链路跟踪——Sleuth

Sleuth 本专栏学习内容来自尚硅谷周阳老师的视频 有兴趣的小伙伴可以点击视频地址观看 随着微服务越来越多&#xff0c;可能会出现A调B&#xff0c;B调C、D等多重调用的情况&#xff0c;出现问题不易排查。 Spring Cloud Sleuth提供了一套完整的服务跟踪的解决方案&#xff0c…

软件测试高频面试题【附答案解析】

面试指导 软件测试理论刷题篇mysql数据库刷题库linux操作系统刷题篇软件测试工程师面试篇 一. 软件测试理论刷题篇 1.软件测试的意义是什么&#xff1f; 思路&#xff1a;什么是软件测试→软件测试的含义 什么是软件测试&#xff1a;在规定的条件下对程序进行操作&#xf…

【错误:A component required a bean of type ‘xxx‘ that could not be found.解决办法】

在学谷粒商城项目的时候出现了以下问题&#xff1a; *************************** APPLICATION FAILED TO START *************************** Description: A component required a bean of type org.redisson.Redisson that could not be found. Action: Consider defining a…

优思学院|西门子精益六西格玛的历程

最新阅读了一份案例报告&#xff0c;报告中仔细研究了西门子公司实施精益六西格玛的历程&#xff0c;也谈到它们利用了线上课程后&#xff0c;取得了更大的成功。 2014年&#xff0c;西门子工业自动化部门&#xff08;IA&#xff09;的高管们认识到他们必须采取措施来加强内部效…

English Learning - L2 第 16 次小组纠音 弱读和语调 2023.4.22 周六

English Learning - L2 第 16 次小组纠音 弱读和语调 2023.4.22 周六 共性问题help /help/ 中的 e 和 lsorry /ˈsɒri/ 中的 ɒ 和 ilook out /lʊk aʊt/ 中的 ɒ 和 aʊdont /dəʊnt/ 中的 əʊemergency /ɪˈmɜːʤənsɪ/ 中的 ɜːname /neɪm/ 中的 eɪright /raɪt/…

甘肃vr全景数字化展厅提高企业品牌认知度和销售效果

相比传统式展厅给观众们呈现的是静态的视觉体会&#xff0c;缺乏实时交互水平。而720VR全景虚拟展厅能够提供高度真实的展览体验&#xff0c;融合视、听、触等各种感官享受&#xff0c;带来颠覆的沉浸式体验。 即便社恐的你也能在虚拟现实的世界游刃有余&#xff0c;想看哪里点…

flutter protobuf插件的安装和使用

1.安装插件 2.在pubspec.yaml添加插件 protobuf: ^2.1.0protoc_plugin: ^20.0.13.安装protoc brew install protobuf检查是否安装成功 protoc --version4.安装dart brew tap dart-lang/dart brew install dartdart 安装好后&#xff0c;就有pub命令了。输入dart 命令行 和d…

【Linux】Linux背景常见的基本指令

文章目录 一、Linux背景二、Linux下基本指令ls 指令pwd 命令cd 指令tree 指令touch 指令mkdir 指令rmdir 指令rm 指令man 指令cp 指令mv 指令cat 指令more 指令less 指令head 指令tail 指令date 指令cal 指令find 指令grep 指令zip 指令unzip 指令tar 指令bc 指令uname 指令重要…

uniapp初始环境搭建,出于猎奇,也出于热爱编程

1 安卓环境 官方文档有些看不懂&#xff0c;这里还是自己写一下&#xff0c;毕竟我已经不想在回来带angular了&#xff0c;还是使用vue3吧&#xff0c;年纪打了&#xff0c;没用的知识学多了也是浪费时间。出于猎奇&#xff0c;也出于热爱编程。 1.1 生成签名证书 Android平台签…

linux系统启动过程与0号和1号进程

讲到linux 0号进程和1号进程就涉及到linux 系统的启动&#xff0c;我们就从linux启动过程开始。 1、linux 启动整体过程 当系统第一次启动或重启的时候&#xff0c;处理器将执行一个已知地方的代码。对应个人电脑&#xff0c;这个地方是存在主板上内存内的BIOS 当一个启动设备…

元宇宙的应用领域

应用领域一&#xff1a;游戏 1.游戏是最先成长起来的元宇宙场景。虚拟社交身份、开放性、经济系统、沉浸感、世界可持续性是元宇宙游戏需关注的五大特征。 2.元宇宙游戏依然是游戏&#xff0c;现阶段参与元宇宙游戏的主要是游戏爱好者。新的概念依旧需要好的游戏产品支撑。团…