C# 特性(Attribute)总结

news/2024/11/15 1:37:30/

目录

特性是什么?

如何使用特性?

(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.ppmy.cn/news/497376.html

相关文章

超好看的UI云开发壁纸小程序源码

正文: 本壁纸表情包头像小程序采用&#xff08;dcloud云开发&#xff09;所以无需服务器与域名&#xff0c;本次版本集合了前两个版本所有的功能并有以下的更新。 更改界面UI样式&#xff0c;修复了路径控制BUG。新增任务中心、首页栏目增加动态壁纸搜索(支持关键词搜索全网动…

导向滤波 Guided Filter 的 CUDA GPU版本

Guided Filter Using CUDA GitHub Repo : Plumess/Guided-Filter-Using-CUDA: A GPU version implementation of Guided Filter, using CUDA C/C, calculates 1080P images in 10ms on 4090 (github.com) 这是导向滤波/引导滤波的一种GPU实现&#xff0c;经测试&#xff0c;在…

有没有关于python的壁纸_初学Python——01(想要好看的壁纸吗?)

#把win10锁屏页面的图片批量导出图片到E&#xff1a;\win10壁纸 import os import shutil pathrC:\Users\Administrator\AppData\Local\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets to_pathrE:\Win10壁纸 #创建文件夹 isExistso…

vue 常见问题处理

当使用Vue.js开发应用程序时&#xff0c;可能会遇到一些常见问题。以下是一些常见的Vue.js问题及其解决方法的集锦&#xff1a; Vue组件无法正常显示或渲染&#xff1a; 确保Vue组件被正确导入和注册。 检查模板语法是否正确&#xff0c;包括HTML标记、属性和指令的使用。 使…

【AUTOSAR】AUTOSAR开发工具链(二)----TASKING库的封装

1、集成工程 步骤&#xff1a; 拷贝模块代码&#xff1a; 将源工程的所有模块代码拷贝到库工程&#xff0c;将源工程拷贝一份&#xff0c;并删除不必要的文件作为释放工程&#xff0c;完成结果如下图&#xff1a; 源工程&#xff08;左&#xff09;VS库工程&#xff08;中&am…

<C++> C++11右值引用

C11右值引用 1.左值引用和右值引用 传统的C语法中就有引用的语法&#xff0c;而C11中新增了的右值引用语法特性&#xff0c;所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用&#xff0c;都是给对象取别名。 什么是左值&#xff1f;什么是左值引用…

2022春节红包封面大全!

虎年春节一大批红包封面来袭&#xff0c;精美图片等你来领。数码宝贝&#xff0c;王嘉儿&#xff0c;张艺兴&#xff0c;娜扎&#xff0c;赵丽颖&#xff0c;陈小春&#xff0c;应采儿 给您拜年啦&#xff01;&#xff01;&#xff01; 【Java架构师之路】公众号回复【0201】即…