C# 自定义特性(Attribute)详解
什么是特性
特性的定义:公共语言运行时允许添加类似关键字的描述声明,叫做attribute,它对程序中的元素进行标注,如类型、字段、方法、和属性等。attribute和.NetFramework文件的元数据保存在一起,可以用来在运行时描述你的代码,或者在程序运行的时候影响应用程序的行为。
如何编写自定义特性
为了帮助大家理解自定义的特性,首先带大家了解一下编译器遇到代码中某个应用了自定义特性时,是如何处理的,以检验Model为例,假如声明一个C#属性,如下
    public class User
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 邮箱
        /// </summary>
        [EmailAttribute]
        public string Email { get; set; }
        /// <summary>
        /// 薪水
        /// </summary>
        [LenghtAttribute(10000, 100000)]
        public decimal Salary { get; set; }
    }
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = = false, Inherited = false)]
    public class LenghtAttribute
    {
        private int _minLenght = 0;
        private int _maxLenght = 0;
        public LenghtAttribute(int minLenght, int maxLenght)
        {
            _minLenght = minLenght;
            _maxLenght = maxLenght;
        }
    }
当编译器发现这个属性应用了一个FiedlName特性时,先会将字符串Attribute追加到这个名称上,形成一个组合名称FieldNameAttribute,然后搜索路径的所有名称空间中搜索有指定该名称的类,但是注意,如果编译器发现了有某个特性标记了数据项,并且该名称为Attribute结尾,编译器不会将该字符加到组合名称中,所以这就是我们用特性标注字段是编译器会默认将Attribute字符以浅蓝色字体显示的原因
之后编译器会找到含有该名称的类,且这个类直接或间接派生自System.Attribute。编译器还认为这个类包含控制特性用法的信息。特别是
- 特性可以应用到那些类型的程序元素上
 - 它是否可以多次应用到同一个程序元素上
 - 特性在应用到类或接口上时,是否由派生类类和接口继承
 - 这个特性有哪些必选参数和可选参数
 
如果找不到指定的特性类,或者找到一个特性类,但使用特性的方式和特性类中的信息不匹配,编译器就会产生一个错误,比如特性类指定该特性只能应用于结构上,但我们应用在字段或者类上,就会产生编译错误
一、指定AttributeUsage类
首先,特性类本身是需要用一个特性--system.AttributeUsage特性来标记的,AttributeUsage用来表示我们的特性可以应用在那些类型的程序元素上,这个参数在AttributeUsage是必选参数,类型是枚举类型 AttributeTargets。具体如下
1.Assembly = 0x1,
2.Module = 0x2,
3.Class = 0x4,
4.Struct = 0x8,
5.Enum = 0x10,
6.Constructor = 0x20,
7.Method = 0x40,
8.Property = 0x80,
9.Field = 0x100,
10.Event = 0x200,
11.Interface = 0x400,
12.Parameter = 0x800,
13.Delegate = 0x1000,
14.ReturnValue = 0x2000,
15.GenericParameter = 0x4000,
16.All = 0x7FFF
如果大家想了解枚举对应的类型可以去System.AttributeTargets下看到详细介绍,这里就不给大家一一介绍了
但是大家需要注意,在上面的枚举类型中有两个枚举值不对应任何程序元素, Assembly和Module
特性可以应用到整个程序集或者模块中,而不是应用在代码中的某一个元素上,在这种情况下,这个特性可以放在源代码的任何地方,但需要使用Assembly和Module,写法如下
    public class User
    {
        [Assembly: FieldNameAttribute(Parameters)]
        [Module: FieldNameAttribute(Parameters)]
    }
另外还有两个属性AllowMultiple和Inherited
AllowMultiple表示该特性是否可以多次运用到同一项上,
Inherited表示应用到类或者接口上的特性可以自动应用到所有派生的类和接口上,如果应用到方法和属性上,也可以自动应用到该方法和属性的重写版本上,
二、指定特性参数
接下来给大家介绍如何自定义特性接受的参数。
编译器会检查传递给特性的参数,并查找该特性中带这些参数类型的构造函数,如果编译器找到了这样的构造函数,编译器就会将指定的元数据传递给程序集,反之就会生成一个编译错误。
三、指定特性的可选参数
在AttributeUsaege特性中,可以使用另外一种语法,把可选参数添加到特性中,这种语法指定可选参数的名称和值,它通过特性类中的公共属性和字段起作用。
四、完整代码
  public abstract class BaseAttribute : Attribute
    {
        /// <summary>
        /// 验证
        /// </summary>
        /// <param name="oValue"></param>
        /// <returns></returns>
        public abstract bool Validate(object oValue);
    }
    /// <summary>
    /// 特性扩展
    /// </summary>
    public static class AttributeExtend
    {
        /// <summary>
        /// 验证
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static bool Validate<T>(T t)
        {
            Type type = t.GetType();
            foreach (var prop in type.GetProperties())
            {
                if (prop.IsDefined(typeof(BaseAttribute), true))
                {
                    var oValue = prop.GetValue(t, null);
                    foreach (BaseAttribute item in prop.GetCustomAttributes(typeof(BaseAttribute), true))//获取字段所有的特性
                    {
                        if (!item.Validate(oValue))
                        {
                            return false;
                        }
                    }
                }
            }
            return true;
        }
    }
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = = false, Inherited = false)]
    public class LenghtAttribute : BaseAttribute
    {
        private int _minLenght = 0;
        private int _maxLenght = 0;
        public LenghtAttribute(int minLenght, int maxLenght)
        {
            _minLenght = minLenght;
            _maxLenght = maxLenght;
        }
        public override bool Validate(object oValue)
        {
            decimal.TryParse(oValue.ToString(), out var minParam);
            decimal.TryParse(oValue.ToString(), out var maxParam);
            if (oValue != null)
            {
                return minParam >= _minLenght && maxParam <= _maxLenght;
            }
            return true;
        }
    }
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = = false, Inherited = false)]
    public class EmailAttribute : BaseAttribute
    {
        public override bool Validate(object oValue)
        {
            if (oValue != null)
            {
                Regex r = new Regex(@"^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$");
                return r.IsMatch(oValue.ToString());
            }
            return true;
        }
    }
     static void Main(string[] args)
        {
            var user = new User { Name = "张三", Email = "1300000@qq.com", Salary = 60000.1M };
            var result = AttributeExtend.Validate<User>(user);
            Console.WriteLine("Hello World!");
            Console.ReadKey();
        }
如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧
C# 自定义特性(Attribute)详解的更多相关文章
- 学习笔记: 特性Attribute详解,应用封装
		
/// /// 特性:中括号声明 /// /// 错觉:每一个特性都可以带来对应的功能 /// /// 实际上特性添加后,编译会在元素内部产生IL,但是我们是没办法直接使用的, /// 而且在meta ...
 - .Net Attribute详解(一)
		
.Net Attribute详解(一) 2013-11-27 08:10 by JustRun, 1427 阅读, 14 评论, 收藏, 编辑 Attribute的直接翻译是属性,这和Property ...
 - C#中的Attribute详解(下)
		
原文地址:https://blog.csdn.net/xiaouncle/article/details/70229119 C#中的Attribute详解(下) 一.Attribute本质 从上篇里我 ...
 - JDK19新特性使用详解
		
前提 JDK19于2022-09-20发布GA版本,本文将会详细介绍JDK19新特性的使用. 新特性列表 新特性列表如下: JPE-405:Record模式(预览功能) JPE-422:JDK移植到L ...
 - .Net Attribute详解(下) - 使用Attribute武装枚举类型
		
接上文.Net Attribute详解(上)-Attribute本质以及一个简单示例,这篇文章介绍一个非常实用的例子,相信你一定能够用到你正在开发的项目中.枚举类型被常常用到项目中,如果要使用枚举To ...
 - BaseAdapter自定义适配器——思路详解
		
BaseAdapter自定义适配器——思路详解 引言: Adapter用来把数据绑定到扩展了AdapterView类的视图组.系统自带了几个原生的Adapter. 由于原生的Adapter视图功能太少 ...
 - 如何获取类或属性的自定义特性(Attribute)
		
如何获取类或属性的自定义特性(Attribute) 问题说明: 在ActiveRecord或者其他的ORM等代码中, 我们经常可以看到自定义特性(Attribute)的存在(如下面的代码所示) [Pr ...
 - .Net Attribute详解(上)-Attribute本质以及一个简单示例
		
Attribute的直接翻译是属性,这和Property容易产生混淆,所以一般翻译成特性加以区分.Attribute常常的表现形式就是[AttributeName], 随意地添加在class, met ...
 - DOM中 property 和 attribute 详解
		
被问到 property 和 attribute 的区别,想来也是要好好看一下. 一.基本概念区别 其实Attribute和Property这两个单词,翻译出来都是“属性”,<js高级程序设计& ...
 
随机推荐
- learning scala generic classes
			
package com.aura.scala.day01 object genericClasses { def main(args: Array[String]): Unit = { val sta ...
 - simcom7600ce-t LBS function
			
Welcome to minicom 2.7 OPTIONS: I18n Compiled on Nov 15 2018, 20:20:38.Port /dev/ttyUSB2, 00:55:23 P ...
 - hud 6184
			
$n$ 点 $m$ 边的图求多少对三元环公用一条边变无向图为有向图 建图方法:对于每条无向边 度数小的端点向度数大的端点连边度数相同则编号小的点向编号大的点连边这样就构成 $DAG$遍历: 遍历每条边 ...
 - TCP/IP协议11种状态
			
1.l SYN_SENT :这个状态与SYN_RCVD 状态相呼应,当客户端SOCKET执行connect()进行连接时,它首先发送SYN报文,然后随即进入到SYN_SENT 状态,并等待服务端的发 ...
 - bochs调试命令
			
Bochs几条基本指令: 通过物理地址查看内存时,可以不加参数'/nuf': 其中n指定显示的单元数,默认是1: u 指定每个显示单元的大小(b表示字节.h表示字(2字节).w表示双字(4字节)),默 ...
 - 转载:CentOS 6.5下安装Python 3.5.2(与Python2并存)
			
原文:https://www.jb51.net/article/115370.htm 这篇文章主要给大家介绍了在CentOS 6.5下安装Python 3.5.2的方法教程,安装后的python3与P ...
 - Tkinter 之Button标签
			
一.参数说明 语法 作用 Button(root,text='xxxx') 按钮图标显示内容 Button(root,text='xxxx',height=2) 组件的高度(所占行数) Button( ...
 - ES里设置索引中倒排列表仅仅存文档ID——采用docs存储后可以降低pos文件和cfs文件大小
			
index_options The index_options parameter controls what information is added to the inverted index, ...
 - springMVC_注解方式搭建基础环境
			
---恢复内容开始--- 一.jar包环境,web配置文件和Spring-MVC配置文件的,相关的modelAndview 1.配置DispatcherServlet <servlet> ...
 - 持续集成和部署工具GOCD
			
如果大家使用过Jenkins那么相信大家对于持续集成非常熟悉.今天要给大家介绍的是另一个非常强大的CD工具GoCD官方对其也称之为GO但是要明白他和go语言golang是没有多大关系的,他是使用jav ...