C# 特性(Attribute)

一、特性(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 ,在新增的类中实现具体的校验逻辑,并且在需要校验的属性上标记对应的特性即可,方便代码扩展

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

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

相关文章

AI智能课程第一讲:chatgpt介绍

AI应用现状 用AI艺术创作 一个小女孩打折手电筒在侏罗世纪公园找恐龙。 AI用于医疗行业 AI辅助驾驶 AI广告投放上的应用 什么是chatgpt&#xff1f; chatgpt相关技术的发展 为什么用chatgpt写代码会特别的快呢&#xff1f; 因为它集成了GitHub上所有开发者的库公用资源&…

供需两端催化口腔医疗服务市场增长 未来将呈现线上化、智能化、品质化三大趋势

一、口腔医疗服务行业概述 口腔由唇、颊、舌、腭、涎腺、牙和颌骨等部分组成。口腔疾病种类繁多&#xff0c;伴随人全生命周期&#xff0c;常见疾病有龋病、牙周疾病、牙髓病、根尖周病、牙齿缺损、错颌畸形等&#xff0c;多数口腔疾病的发病率高&#xff0c;诊疗需求大。除此…

原型设计工具即时设计、Axure、Figma、Sketch,哪个更好用?

在线网页原型图设计软件的使用与桌面端相比具备优势&#xff0c;因为在线网页原型图设计软件的使用全程不需要安装&#xff0c;而且在线网页原型图设计软件也没有任何地点上的限制&#xff0c;更主要的是在线网页原型图设计软件在操作系统上也没有限制&#xff0c;不论是现在使…

GPT模型支持下的Python-GEE遥感云大数据分析、管理与可视化技术应用

随着航空、航天、近地空间等多个遥感平台的不断发展&#xff0c;近年来遥感技术突飞猛进。由此&#xff0c;遥感数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量也大幅增长&#xff0c;使其越来越具有大数据特征。对于相关研究而言&#xff0c;遥感大数据的出现为其提…

服务型企业如何使用飞项实现项目化管理?

服务型企业的业务模式一般都是按项目来运作的&#xff0c;其业务分为售前&#xff0c;售中和售后三个阶段&#xff0c;分别由不同部门和人员对客户进行个性化服务。在这个过程中需要对人、流程和知识的高效统筹管理&#xff0c;即项目的整体管理&#xff0c;因此存在着不小的挑…

git lfs简易使用教程

参考资料&#xff1a; https://zzz.buzz/zh/2016/04/19/the-guide-to-git-lfs/ 这篇随笔简单记录一下git lfs的使用教程&#xff0c;只记录最为常用的部分&#xff0c;并阐述原理&#xff0c;方便后面查阅。 首先说明一下git lfs的原理&#xff0c;看名称&#xff1a;git lfs。…

算法:(力扣)(牛客)打印螺旋矩阵题

手撕螺旋矩阵 题目思路解题 题目 描述&#xff1a;给定一个m x n大小的矩阵&#xff08;m行&#xff0c;n列&#xff09;&#xff0c;按螺旋的顺序返回矩阵中的所有元素。数据范围&#xff1a;0 \le n,m \le 100≤n,m≤10&#xff0c;矩阵中任意元素都满足 |val| \le 100∣val…

如何优化语音交友app开发的搜索和匹配算法

语音交友app开发的挑战 在当今社交媒体行业中&#xff0c;语音交友app开发已经成为一个热门的领域。越来越多的人开始使用语音交友app来寻找新的朋友&#xff0c;这也为开发者们带来了许多机会。然而&#xff0c;这个领域也面临着一些挑战。其中一个最大的挑战是如何优化搜索和…

全志v851s uart3 设置成普通串口收发

本文转载自&#xff1a;https://bbs.aw-ol.com/topic/3281/ 由于UART0 被设定为系统dubug 输出&#xff08;简单来说就是将ttyS0 设定为console&#xff09;&#xff0c;所以使用UART3 作为普通的串口&#xff0c;进行与别的设备通信。 1. 查看硬件电路图SCH_Schematic1_2022…

springboot 接口防刷(根据IP与路径限制)

接口防刷 一、全局接口防刷&#xff08;通过拦截器方式&#xff09;1、原理 代码示例 二、个别接口防刷&#xff08;接口注解方式)1、代码示例 一、全局接口防刷&#xff08;通过拦截器方式&#xff09; 1、原理 代码示例 通过ip地址uri拼接用以作为访问者访问接口区分通过…

Vivado中VIO IP核的使用

Vivado中VIO IP核的使用 一、写在前面二、VIO IP核配置三、VIO联调四、写在后面 一、写在前面 Vivado中的VIO&#xff08;Virtual Input/Output&#xff09; IP核是一种用于调试和测试FPGA设计的IP核。它允许设计者通过使用JTAG接口读取和写入FPGA内部的寄存器&#xff0c;从而…

复旦大学郁喆隽:网络制造出人的“幻象”,深度思考如何可能?

“人是什么?”这是亘古以来人们反复追问的一个古老命题。从元宇宙到ChatGPT&#xff0c;这个人人都在讨论、理解和实践互联网的时代&#xff0c;对“人”的自我定义和认知产生了哪些影响&#xff1f;    在3月12日复旦大学-华盛顿大学EMBA项目主办的“复调艺文沙龙”上&am…

Keil生成.bin文件

1. 打开OptionsforTarget 对话框&#xff1a; 2 选择User&#xff1a; 3. 根据User页的配置还要配置Output页面&#xff0c;具体如下&#xff1a; 4. 点击OK确定&#xff0c;然后再重新编译则会按照上图中的配置路径生成.bin格式的文件了&#xff1a; Keil自带的fromelf.exe工具…

Python3 OpenCV4 计算机视觉学习手册:6~11

原文&#xff1a;Learning OpenCV 4 Computer Vision with Python 3 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 计算机视觉 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 当别人说你没有底线的时候&a…

Git 时间线管理

Git 时间线管理 这一部分主要讲的是 取消(undo) 变化 和在不同的时间锚点跳来跳去&#xff0c;以 command 为主。 设计到的commits有&#xff1a; checkoutrestoreresetrevert checkout checkout 的一部分作用&#xff0c;即切换分枝在 git 分支操作 中有提到过&#xff0…

【SCI电气】考虑不同充电需求的电动汽车有序充电调度方法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

从单兵作战到生态共创,纵目科技打响智驾2.0新战役

4月18日&#xff0c;第十二届上海国际汽车工业展览会&#xff08;简称&#xff1a;2023上海车展&#xff09;在上海国家会展中心盛大启幕。纵目科技携最新自动驾驶解决方案——Amphiman 3000、8000行泊一体解决方案、Trinity 3000、8000舱行泊一体解决方案以及众多摄像头产品强…

V2.4版本商超标签专用路由器

PICK_Router_V2.4 产品参数 产品型号 PICK_Router_V2.4 尺寸(mm) 21*14*4.3mm 工作温度 -10-70℃ 产品重量 465g 供电方式 DC12V or POE 工作频率 2.4G 通信速率 50-250kbps 通信方式 10/100Mbps有线网络&2.4G 通信半径 30m 支持标签数量 >10000…

opengl绘制三角形

1.绘制两个三角形 GLfloat vertices1[] { 0.5f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, -0.5f, 0.5f, 0.0f } GLfloat vertices2[] { 0.5f, -0.5f, 0.0f, -0.5f, 0.5f, 0.0f&#xff0c; -0.5f, -0.5f, 0.0f } 也可以用索引的方式&#xff1a; GLfloat vertices[] { 0.5f, 0.5f, 0…

浅谈数字化工厂五大核心系统

一、什么是数字化工厂 数字化工厂是将数字技术应用于工厂生产、管理和运营中的一种方式&#xff0c;可以帮助企业提高生产效率和质量&#xff0c;降低成本和风险&#xff0c;提高竞争力和市场份额。数字化工厂是中小制造业企业自主建设制造业信息化的途径。 简道云数字化工厂解…