C# 特性总结

news2024/10/6 15:03:29

目录

特性是什么?

如何使用特性?

(1).Net 框架预定义特性

(2)自定义特性

为什么要使用特性?

特性的应用

特性实现枚举展示描述信息


特性是什么?

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

1 特性定义:特性其实是一个类,是直接/间接继承自Attribute,约定俗成是以Attribut结尾,在标记的时候以[   ]包裹,尾部的Attribut可以省略掉;

2 特性的作用:可以增加额外信息,增加额外功能,但让特性生效需要使用反射

以上都是较为官方的解释,从本人理解来看:特性的作用就是对程序中所标记元素的另加描述和限限制。就像生活中的坐火车的车票一样,对该行程进行了进一步描述(如哪个站,目的地,出发时间等),同时也对描述信息进行了限制(如你错过发车时间就不能上车),出现任何差错都会视为异常。

如何使用特性?

(1).Net 框架预定义特性

  • Conditional:起条件编译的作用,只有满足条件,才允许编译器对它的代码进行编译。一般在程序调试的时候使用
  • DllImport: 用来标记费.net的函数,表明该方法在一个外部的DLL中定义。
  • Obsolete: 这个属性用来标记当前的方法已经废弃,不再使用。
  • Serializable:用来标记该对象可以被序列化。
  • AttributeUsage :描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型:

 /// <summary>
/// 自定义特性
/// AllowMultiple =true:标记在特性上的特性,其实是对特性的一种约束;
/// Inherited =true:约束当前特性是否可以继承
/// AttributeTargets.All:当前特性可以标记在所有的元素上
/// AttributeUsage:在定义特性的时候,对特性的一种约束
/// </summary>
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
public class CustomAttribute : Attribute
{
        public CustomAttribute()
        {

        }
        
    }
}

(2)自定义特性

.Net 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。创建并使用自定义特性包含四个步骤:

  1. 声明自定义特性
  2. 构建自定义特性
  3. 在目标程序元素上应用自定义特性
  4. 通过反射调用特性

▲自定义一个特性:

  • 定义一个类并且这个类派生自Attribute。
  • 类的名字以Attribute后缀结构。
  • 为了安全性,可以用一个sealed修饰成一个密封类型(非必须的),以防止被其他类所继承(可以参考预定义特性的源码,其都被sealed所修饰)
using System;

namespace MyAttribute
{    
    public sealed class CustomAttribute : Attribute
    {
        
    }
}

▲通过反射调用标记到字段,属性,方法上的特性:

通过反射,从类型,属性,方法都可以获取特性实例,要求先IsDefined检测再获取实例化。

public static void ReflectionArrtibute<T>(T t) where T : Class
{
      Type type = t.GetType();//通过参数来获取Type类型
      if (type.IsDefined(typeof(CustomAttribute),true)) //反射调用特性,记得一定要先判断,然后再获取
      { 
          foreach (CustomAttribute attribute in type.GetCustomAttributes()) //把所有标记的特性都实例化了
          {
              Console.WriteLine($"attribute.Id={attribute.Id}");
              Console.WriteLine($"attribute.Name={attribute.Name}");
              attribute.Do();
          }
          ///循环获取所有的属性上标记的特性,调用特性中的元素
          foreach (PropertyInfo prop in type.GetProperties())
          { 
              if (prop.IsDefined(typeof(CustomAttribute), true))
              {
                  foreach (CustomAttribute attribute in prop.GetCustomAttributes()) //把所有标记的特性都实例化了
                  {
                      Console.WriteLine($"attribute.Id={attribute.Id}");
                      Console.WriteLine($"attribute.Name={attribute.Name}");
                      attribute.Do();
                  }
              }
          }
          ///循环获取所有的字段上标记的特性,调用特性中的元素
          foreach (FieldInfo field in type.GetFields())
          {
              if (field.IsDefined(typeof(CustomAttribute), true))
              {
                  foreach (CustomAttribute attribute in field.GetCustomAttributes()) //把所有标记的特性都实例化了
                  {
                      Console.WriteLine($"attribute.Id={attribute.Id}");
                      Console.WriteLine($"attribute.Name={attribute.Name}");
                      attribute.Do();
                  }
              }
           }
          ///循环获取所有方法上标记的特性,调用特性中的元素
          foreach (MethodInfo method in type.GetMethods())
          {
              if (method.IsDefined(typeof(CustomAttribute), true))
              {
                  foreach (CustomAttribute attribute in method.GetCustomAttributes()) //把所有标记的特性都实例化了
                  {
                      Console.WriteLine($"attribute.Id={attribute.Id}");
                      Console.WriteLine($"attribute.Name={attribute.Name}");
                      attribute.Do();
                  }
              }
          } 
}

为什么要使用特性?

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

       if (string.IsNullOrWhiteSpace(user.Email))  //:这里只判断了邮箱为空,省略了邮箱合法性判断
        {
         Console.WriteLine("Email 参数不符合!");
         //:这里不执行保存,提示用户参数不合法
        }
        if (user.CompanyId < 1000 || user.CompanyId > 10000)
        {
          Console.WriteLine("CompanyId 参数不符合,CompanyId范围只能是1000~10000");
          //:这里不执行保存,提示用户参数不合法
        }

问题:假如某一天业务需求发生了变化,新增了一个参数的校验或者CompanyId的范围发生改变,我们就需要修改代码。

解决:使用特性:可以在不破坏类型封装的前提下,为对象增加额外的信息,执行额外的行为。通过特性我们把公共逻辑移出去,只完成私有逻辑,具体操作流程如下:

1、考虑到程序后期可能会涉及到多种参数的校验,每种参数要实现的校验规则各不相同,所以我们定义一个抽象类,抽象类中定义一个抽象方法,继承自Attribute。

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

2、分别定义两个类用于实现校验邮箱和判断公司ID范围的逻辑,并继承自上面的抽象类。

    //邮箱合法
    [AttributeUsage(AttributeTargets.Property)]
    public  class EmailValidateAttribute:AbstractValidateAttribute
    {
        public override bool Validate(object value)
        {
            if (value!=null)
            {
                return true;//实际的判断
            }
            else
            {
                return false;
            }
        }
    }
    //公司ID在1000~10000范围
    [AttributeUsage(AttributeTargets.Property)]
    public class IntValidateAttribute:AbstractValidateAttribute
    {
        private int _Min = 0;
        private int _Max = 0;

        public IntValidateAttribute(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、通过反射调用特性(采用泛型)

 public  class BaseDAL
    {
        public static void Save<T>(T t)
        {
            Type type = t.GetType();
            PropertyInfo[] T2 = type.GetProperties();
            bool isSafe = true;
            {
                foreach (var property in type.GetProperties())
                {
                    //特性类的实例化就在反射发生的时候
                    object[] oAttributeArray = property.GetCustomAttributes(typeof(AbstractValidateAttribute), true);
                    foreach (var oAttribute in oAttributeArray)
                    {
                        //转为基类对象
                        AbstractValidateAttribute validateAttribute = oAttribute as AbstractValidateAttribute;
                        isSafe = validateAttribute.Validate(property.GetValue(t));
                        if (isSafe)
                        {
                            Console.WriteLine($"{oAttribute.ToString()}保存到数据库");
                        }
                        else
                        {
                            Console.WriteLine($"{oAttribute.ToString()}数据不合法");
                        }
                    }
                   
                }
            }
          
        }
    }

4、构建业务类UseModel,并标记相关特性特性

 public  class UseModel
   {
        //邮箱
        [EmailValidate]
        public string Email { get; set; }
        //企业ID
        [IntValidate(1000,10000)]
        public int CompanyID { get; set; }

        public UseModel(string email,int id)
        {
            this.Email = email;
            this.CompanyID = id;
        }
    }

5、主程序调用

        static void Main(string[] args)
        {
            UseModel useModel = new UseModel("小米", 120000);
            BaseDAL.Save<UseModel>(useModel);
        }

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

特性的应用

特性实现枚举展示描述信息

(1)创建枚举描述特性RemarkAttribute

  [AttributeUsage(AttributeTargets.Field)]
    public class RemarkAttribute:Attribute
    {
        public string Remark { get; set; }
        public RemarkAttribute(string remark)
        {
            this.Remark = remark;
        }
    }

(2)创建枚举(有三个状态:正常,冻结,删除),并进行标记

  public enum UserState
    {
        //正常状态
        [Remark("正常状态")]
        Normal =0,
        //冻结状态
        [Remark("冻结状态")]
        Frozen =1,
        //删除状态
        [Remark("删除状态")]
        Deleted = 2

    }

(3)通过反射调用特性

    public static class AttributeExtend
    {
        public static string GetRemark(this Enum value)
        {
            Type type = value.GetType();
            var field = type.GetField(value.ToString());
            if (field.IsDefined(typeof(RemarkAttribute), true))
            {
                RemarkAttribute attribute = (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute), true);
                return attribute.Remark;
            }
            else
            {
                return value.ToString();
            }
        }
    }

(4)主程序调用

       static void Main(string[] args)
        {
           UserState userState = UserState.Frozen;
           string reamrk = userState.GetRemark();
           Console.WriteLine(reamrk);
        }
//打印结果:“冻结状态”

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

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

相关文章

拉新、转化、留存,一个做不好,就可能会噶?

用户周期 对于我们各个平台来说&#xff08;CSDN也是&#xff09;&#xff0c;我们用户都会有一个生命周期&#xff1a;引入期–成长期–成熟期–休眠期–流失期。 而一般获客就在引入期&#xff0c;在这个时候我们会通过推广的手段进行拉新&#xff1b;升值期则发生在成长期…

智能制造工厂的SCADA解决方案应用

智能制造工厂是当今工业领域的一个重要趋势&#xff0c;它将传统的生产模式与现代信息技术相结合&#xff0c;实现了生产过程的智能化和自动化。 SCADA是一种监控与数据采集系统&#xff0c;广泛应用于工业自动化领域&#xff0c;它通过传感器、控制器和网络等设备&#xff0c…

基于Python机器学习算法小分子药性预测(岭回归+随机森林回归+极端森林回归+加权平均融合模型)

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境配置工具包 模块实现1. 数据预处理2. 创建模型并编译3. 模型训练 系统测试工程源代码下载其它资料下载 前言 《麻省理工科技评论》于2020年发布了“十大突破性技术”预测&#xff0c;其中包括“AI药物分子发现”…

一文说透!华熙生物如何步步为营炼就品牌势能?

据华熙生物2022年财报&#xff0c;华熙生物2022年营收同比增长28.53%&#xff0c;净利润同比增长24%&#xff0c;成为全球最大的。同时&#xff0c;近年来也在C端也大展身手。华熙生物此前与故宫博物院合作&#xff0c;推出6 款故宫国宝色口红和2款“故宫美人面膜”。凭借精美的…

中小型企业需要官网和帮助中心吗?为何这样说?

随着互联网技术的不断发展&#xff0c;越来越多的中小型企业开始重视拥有自己的官网和帮助中心。但是&#xff0c;对于许多刚刚起步的中小型企业来说&#xff0c;官网和帮助中心的建设可能需要一定的成本和时间投入。那么&#xff0c;中小型企业是否需要官网和帮助中心呢&#…

python(11):python读取excel、csv文件

1.python读取excel文件 要读取Excel表格的指定行和列范围&#xff0c;可以使用Python中的第三方库pandas。pandas库提供了强大的数据分析和处理工具&#xff0c;包括读取和处理Excel文件的功能。以下是一个示例代码&#xff0c;演示了如何使用pandas库读取Excel表格中的指定行…

[Go]-Go语言第一课

1-1 Go语言特点 特点&#xff1a; 1. 静态类型&#xff0c;编译开源语言2. 脚本化的语法&#xff0c;支持多种编程范式&#xff08;函数式&#xff0c;面向对象&#xff09;3. 原生&#xff0c;给力的并发支持并发编程1-2 Go语言优势与劣势 Go语言的优势&#xff1a; 1.脚本化…

软考A计划-系统集成项目管理工程师-信息化知识(三)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

加密市场与上一轮周期有何异同?五大因素探讨加密市场未来之路

数字资产市场在一季度表现不俗&#xff0c;但二季度的表现却出现了相反的情况。数据显示&#xff0c;BTC 在一季度累计上涨了 71.69%&#xff0c;而二季度截至目前下跌了 7.31%。这样的变化主要是由金融监管机构针对整个数字资产行业采取的监管行动造成的。虽然 BTC 今年以来仍…

uniapp中uni-popup的用法——实例讲解

uni-pop弹出层组件&#xff0c;在应用中弹出一个消息提示窗口、提示框等,可以设置弹出层的位置&#xff0c;是中间、底部、还是顶部。 如下图效果所示&#xff1a;白色区域则为弹出的pop层。 一、 创建一个自定义组件&#xff1a; 1.项目中安装下载uni-pop插件。 2.把pop内容…

2023.6.19项目部署(一)前端项目部署

文章目录 项目部署&#xff08;一&#xff09;前端项目一、安装宝塔面板1、安装宝塔面板2、放行端口3、安装相关软件4、添加站点 二、项目打包1、Springboot打包2、vue项目打包 三、前端项目部署1、安装nginx2、修改配置文件 项目部署&#xff08;一&#xff09;前端项目 将开…

SQL优化的几种方法

目录 避免使用select *用union all代替union小表驱动大表批量操作多用limitSQL查找是否"存在"&#xff0c;别再count了&#xff01;in中值太多增量查询高效的分页用连接查询代替子查询join的表不宜过多join时要注意控制索引的数量选择合理的字段类型提升group by的效…

HTTPHTTPS协议详解

目录 一、HTTP是什么&#xff1f; 理解 "应用层协议" 理解 HTTP 协议的工作过程 二、HTTP 协议格式 抓包工具的使用 抓包工具的原理 抓包结果 协议格式总结 三、HTTP 请求 (Request) 认识 URL URL 基本格式 认识 "方法" (method) 认识请求 &quo…

使用Java设计实现一个高效可伸缩的计算结果缓存

目录 概述1.缓存实现1.1 使用HashMapSynchronized实现缓存1.2 使用ConcurrentHashMap代替HashMap改进缓存的并发1.3 完成可伸缩性高效缓存的最终方案1.4 测试代码 2.并发技巧总结 概述 现在的软件开发中几乎所有的应用都会用到某种形式的缓存&#xff0c;重用之前的计算结果能…

回收站数据恢复方法有哪些?五招走起,趁早上手

回收站数据恢复方法是我们在日常操作电脑时不可避免需要面对的问题。本文将对几种常用的回收站数据恢复方法进行介绍&#xff0c;为大家解决恢复回收站数据的常见问题。 一、使用快捷键恢复回收站文件 在我们的电脑中&#xff0c;有很多实用的快捷键&#xff0c;其中有效地恢…

分享干货,多编程语言代码生成神器 CodeGeeX,编码效率提升十倍

CodeGeeX 是一个具有 130 亿参数的多编程语言代码生成预训练模型&#xff0c;采用华为 MindSpore 框架实现&#xff0c;在鹏城实验室“鹏城云脑 II”上使用 1536 个国产昇腾 910 AI 处理器训练而成。 CodeGeexX 支持十多种主流编程语言的高精度代码生成、跨语言代码翻译等功能&…

Django网络空间微博管理信息系统-计算机毕设 附源码85633

Django网络空间微博管理信息系统 摘 要 本论文主要论述了如何使用django框架开发一个网络空间微博管理信息系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述该系统的当前背景以及系统…

MidJourney使用教程:一 第一次怎么用Midjourney

实际我是先写的prompts提示这部分&#xff0c;觉得Midjurney使用的方式&#xff0c;市面上已经有一大把文章了&#xff0c;另一方面觉得也没什么可写的。注册一个discard账号写个prompts描述出图就可以了&#xff0c;但其实有很多点其实忽略掉。比如图出来了&#xff0c;这四幅…

cesium封装实现卫星视锥扫描效果

废话不多说,先看效果 先封装视锥效果函数 // 绘制卫星锥体const radarScanner = (position,height,radarId,bottomRadius,color) => {viewer.entities.add({

基于Springboot+vue的垃圾分类网站设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…