什么是特性


特性的定义:公共语言运行时允许添加类似关键字的描述声明,叫做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。编译器还认为这个类包含控制特性用法的信息。特别是

  1. 特性可以应用到那些类型的程序元素上
  2. 它是否可以多次应用到同一个程序元素上
  3. 特性在应用到类或接口上时,是否由派生类类和接口继承
  4. 这个特性有哪些必选参数和可选参数

如果找不到指定的特性类,或者找到一个特性类,但使用特性的方式和特性类中的信息不匹配,编译器就会产生一个错误,比如特性类指定该特性只能应用于结构上,但我们应用在字段或者类上,就会产生编译错误


一、指定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)详解的更多相关文章

  1. 学习笔记: 特性Attribute详解,应用封装

    /// /// 特性:中括号声明 /// /// 错觉:每一个特性都可以带来对应的功能 /// /// 实际上特性添加后,编译会在元素内部产生IL,但是我们是没办法直接使用的, /// 而且在meta ...

  2. .Net Attribute详解(一)

    .Net Attribute详解(一) 2013-11-27 08:10 by JustRun, 1427 阅读, 14 评论, 收藏, 编辑 Attribute的直接翻译是属性,这和Property ...

  3. C#中的Attribute详解(下)

    原文地址:https://blog.csdn.net/xiaouncle/article/details/70229119 C#中的Attribute详解(下) 一.Attribute本质 从上篇里我 ...

  4. JDK19新特性使用详解

    前提 JDK19于2022-09-20发布GA版本,本文将会详细介绍JDK19新特性的使用. 新特性列表 新特性列表如下: JPE-405:Record模式(预览功能) JPE-422:JDK移植到L ...

  5. .Net Attribute详解(下) - 使用Attribute武装枚举类型

    接上文.Net Attribute详解(上)-Attribute本质以及一个简单示例,这篇文章介绍一个非常实用的例子,相信你一定能够用到你正在开发的项目中.枚举类型被常常用到项目中,如果要使用枚举To ...

  6. BaseAdapter自定义适配器——思路详解

    BaseAdapter自定义适配器——思路详解 引言: Adapter用来把数据绑定到扩展了AdapterView类的视图组.系统自带了几个原生的Adapter. 由于原生的Adapter视图功能太少 ...

  7. 如何获取类或属性的自定义特性(Attribute)

    如何获取类或属性的自定义特性(Attribute) 问题说明: 在ActiveRecord或者其他的ORM等代码中, 我们经常可以看到自定义特性(Attribute)的存在(如下面的代码所示) [Pr ...

  8. .Net Attribute详解(上)-Attribute本质以及一个简单示例

    Attribute的直接翻译是属性,这和Property容易产生混淆,所以一般翻译成特性加以区分.Attribute常常的表现形式就是[AttributeName], 随意地添加在class, met ...

  9. DOM中 property 和 attribute 详解

    被问到 property 和 attribute 的区别,想来也是要好好看一下. 一.基本概念区别 其实Attribute和Property这两个单词,翻译出来都是“属性”,<js高级程序设计& ...

随机推荐

  1. c++学生信息管理系统(window控制台实现鼠标点击操作)

    翻起大一时写过的作业代码--一个学生信息管理系统,当时不会使用QT,不会MFC等库,只会c++,但是又想做一个有界面的,能够实现鼠标操作的程序.于是绞尽脑汁查资料,自己造轮子,最终写出来了下面的这个现 ...

  2. 【概率论】5-1:分布介绍(Special Distribution Introduction)

    title: [概率论]5-1:分布介绍(Special Distribution Introduction) categories: - Mathematic - Probability keywo ...

  3. java.lang.OutOfMemoryError:PermGen space tomcat7 内存溢出

    Tomcat 解决 在启动项目的时候,会报java.lang.OutOfMemoryError:PermGen space错误. 在tomcat/bin/catalina.sh  windows li ...

  4. P1021 邮票面值设计——搜索+完全背包

    P1021 邮票面值设计 题目意思是你最多用n张邮票,你可以自己设定k种邮票的面值,每种邮票数量无穷,你最多能用这k种邮票在不超过n张的情况下,组合成的价值要求是从1开始连续的, 求最大能连续到多少: ...

  5. 激活 phpstorm2019.1 win10

    首先添加以下内容到c:\windows\system32\drivers\etc\hosts 文件 0.0.0.0 account.jetbrains.com 0.0.0.0 www.jetbrain ...

  6. python 使用PyKDL 四元数转欧拉角

    安装: sudo apt-get install ros-indigo-kdl-parser-py 使用: import PyKDLimport math def quat_to_angle(quat ...

  7. zabbix(x)

    问题现象: 客户端设置好自定义监控项,脚本执行或者命令执行都可以正常的输出,但是服务器端通过zabbix-get从客户端获取数据的时候,获取到不正常的值(比如客户端获取到1,服务端获取时显示0或者直接 ...

  8. yum安装nginx添加upstream_check_module模块

    下载模块 upstream_check_module 查看yum安装nginx版本信息 # nginx -V nginx version: nginx/1.17.0 built by gcc 4.8. ...

  9. SpringCloud学习整理

    参考文档 [1]: Spring Cloud Ribbon负载均衡

  10. kafka 45个题目介绍

    >1.Kafka面试问答 Apache Kafka的受欢迎程度很高,Kafka拥有充足的就业机会和职业前景.此外,在这个时代拥有kafka知识是一条快速增长的道路.所以,在这篇文章中,我们收集了 ...