什么是特性


特性的定义:公共语言运行时允许添加类似关键字的描述声明,叫做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. vue+axios新手实践实现登陆

    vue+axios新手实践实现登陆 https://segmentfault.com/a/1190000015201803 增加 利用HTML5的history.replacestate()修改当前页 ...

  2. koa2做请求转发

    最近用koa2做请求转发时,采用了request(options).pipe(ctx.res)的方法,结果出现了有时候前端获得的数据是分片的. 后来翻阅文档,采取了如下方式解决: const Pass ...

  3. wepy框架滑动组件使用

    https://github.com/dlhandsome/wepy-com-swiper

  4. 6.Python3字符串和格式化

    一.字符串 1.字符串表示方法 2.字符串的序号 3.字符串的使用 4.字符串切片 5.字符串的特殊字符 6.字符串操作符 案例:输入对应的数字显示对应的星期 '''weekStr = "星 ...

  5. Input类中常用的验证方式

    Deolin一般将Input类对象作为POST请求方法的参数, Input类的域与前端的数据结构一一对应,由于后端不应该相信前端传过来的任何数据, 所以前端的数据对象先绑定到Input对象中,通过JS ...

  6. 将制定目录家到系统PATH环境变量中

    1.打开系统文件sudo vim/etc/profile,请输入root密码 2.在appendpath '/usr/local/sbin' appendpath '/usr/local/bin' a ...

  7. TopCoder SRM 582 Div 1 - Problem 1000 SemiPerfectPower

    首先我们可以把答案差分,那么我们只需要求出\(1\)~\(x\)范围内的满足条件的数即可. 题目要求的应该是这个东西的个数: \(l \leq a*b^c \leq r(1 \le a < b) ...

  8. JavaScript数字精度丢失的一些问题

    本文分为三个部分 JS 数字精度丢失的一些典型问题 JS 数字精度丢失的原因 解决方案(一个对象+一个函数) 一.JS数字精度丢失的一些典型问题 1. 两个简单的浮点数相加 1 0.1 + 0.2 ! ...

  9. 咏南中间件和开发框架全面支持DELPHI10.3.2

    咏南中间件和开发框架全面支持DELPHI10.3.2 易博龙公司2019年7月12日发布了RAD STUDIO10.3.2正式版本. 咏南中间件自2019年7月14日始,中间件.CS框架.WEB框架. ...

  10. CSRF in asp.net mvc and ap.net core

    如果在方法上添加了[ValidateAntiForgeryToken],没处理好 请求没有带参数 2019-09-17 14:02:45,142 ERROR [36]: System.Web.Mvc. ...