一、什么是特性

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

  特性可以描述我们的代码,或者影响应用程序的行为。特性可以用来处理多种问题,比如序列化、数据验证、程序的安全特征等等。

  特性不是修饰符而是一个有独特实例化形式的类,继承于Attributes基类。其实我们在很多地方都能接触到特性,特性在平时的运用中是非常常见的,比如以下三个场景:

  1.特性[Serializable]标记可序列化的类

  [Serializable]
  public class MyObject { }

  2.特性[ServiceContract]指名WCF中可以用来对外调用的接口

  [ServiceContract]
  public interface IService{}

  3.特性[Range]用于MVC中类的属性的范围

  [Range(, )]
  public int Age { get; set; }//年龄范围

二、预定义特性

  .Net框架已经给我们提供了一些预定义的特性,像是上面的三个场景的三个特性我们就可以直接拿来用。这里我们主要介绍另外三个比较基础的特性,它们都继承Attribute类,分别是:Obsolete、Conditional和AttributeUsage。

  1.Obsolete

  这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。

  • 参数 message,是一个字符串,描述项目为什么过时的原因以及该替代使用什么。
  • 参数 iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)。

  示例如下:

    [Obsolete("该方法已经过时,用NewMethod代替", true)]
public static void OldMethod()
{
Console.WriteLine("OldMethod");
}

  2.Conditional

  Conditional标记了一个条件方法,当满足谋个条件的时候该方法才能执行,多用于程序的调试和诊断。

  示例如下:

    #define Error //宏定义,决定那个方法执行
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text; namespace Attribute.Demo
{
class Program
{
static void Main(string[] args)
{
Debug();
Error();
Console.ReadKey();
}
[Conditional("Debug")]
public static void Debug()
{
Console.WriteLine("Debug");
}
[Conditional("Error")]
public static void Error()
{
Console.WriteLine("Error");
}
}
}

  最后结果是:

    Error

  3.AttributeUsage

  预定义特性 AttributeUsage 描述了如何使用一个自定义特性类。它规定了自定义特性可应用到的项目的类型。这说明了该特性可以描述别的特性,对描述的特性进行某些规定。

  • 参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。
  • 参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。
  • 参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。

  示例如下:

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]

  可以规定多个可放置的语言元素,用标识符 | 分隔开来就行,上述代码就表示描述的特性可以用于属性和字段,如果标注在别的如类上就会报错。

三、自定义特性

  前面有提到预定义的特性都有继承自定义特性的基类Attribute,那么我们自己实现一个自定义特性也就需要继承Attribute类。那突然想到既然特性是一个类,那么为什么直接在描述目标前用方括号声明特性就可以又和一般的类有什么区别呢?主要有以下的一些区别和注意点:

  • 特性的实例化不是通过new的,而是在方括号中调用构造函数。并且构造函数可以有多个,构造函数里的参数为定位参数,定位参数必须放在括号的最前面,按照传入的定位参数可以调用相应的构造函数来实例化,如果有自己定义的构造函数则必须传入定位参数进行实例化否则报错。
  • 特性中属性的赋值,可以通过具名参数赋值,但是具名参数必须在定位参数后面,顺序可以打乱的,具体的形式如ErrorMessage = "年龄不在规定范围内"。

  接下来我就来自己实现验证属性值是否在规定区间内的特性,类似于[Range]。我们定义一个特性MyRangeAttribute继承基类Attribute,用预定义特性AttributeUsage规定只能用于描述属性,并自定义构造函数传入最小值和最大值,并定义方法Validate()校验,具体如下:

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
class MyRangeAttribute : System.Attribute
{
public MyRangeAttribute(int _min, int _max)
{
this.max = _max;
this.min = _min;
}
private int max;
public int Max
{
get; set;
}
private int min;
public int Min
{
get; set;
}
private string errorMessage;
public string ErrorMessage
{
get; set;
}
public bool Validate(int _value)
{
return _value >= min && _value <= max;
}
}

  接下来,我们创建一个Student类里面有Age属性并用我们的自定义特性MyRangeAttribute描述,Student类继承People类,在People类中有方法IsValidate()通过反射执行特性的校验方法Validate(),具体如下:

    class Student: BaseClass
{
private int age;
[MyRange(,, ErrorMessage = "年龄不在规定范围内")]
public int Age
{
get;set;
}
}
class BaseClass
{
public bool IsValidate(out string msg)
{
msg = string.Empty;
Type type = this.GetType();
foreach (var prop in type.GetProperties())
{
foreach (var attribute in prop.GetCustomAttributes())
{
object[] parameters = new object[] { (int)prop.GetValue(this, null) };
if ((bool)attribute.GetType().GetMethod("Validate").Invoke(attribute, parameters))
return true;
else
{
msg = attribute.GetType().GetProperty("ErrorMessage").GetValue(attribute,null).ToString();
return false;
}
}
}
return false;
}
}

  我们在控制台程序中执行如下代码:

    static void Main(string[] args)
{
string msg = string.Empty;
Student student = new Student();
while (true)
{
Console.WriteLine("请输入年龄(输入exit退出):");
string str = Console.ReadLine();
if (str.Equals("exit"))
break;
else
{
student.Age = Convert.ToInt32(str);
if (student.IsValidate(out msg))
Console.WriteLine("验证通过");
else
Console.WriteLine(msg);
}
}
}

  运行可以看到结果如下:

四、结尾

  通过上述的例子可以看出特性可以和反射配合来进行相应的操作,不过反射会消耗性能,并且特性类可以用别的特性描述。

  如果有什么问题可以留言讨论!谢谢阅读。

C#的特性Attribute的更多相关文章

  1. [C#] 剖析 AssemblyInfo.cs - 了解常用的特性 Attribute

    剖析 AssemblyInfo.cs - 了解常用的特性 Attribute [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5944391.html 序 ...

  2. [C#] C# 知识回顾 - 特性 Attribute

    C# 知识回顾 - 特性 Attribute [博主]反骨仔 [原文地址]http://www.cnblogs.com/liqingwen/p/5911289.html 目录 特性简介 使用特性 特性 ...

  3. C# 知识特性 Attribute

    C#知识--获取特性 Attribute 特性提供功能强大的方法,用以将元数据或声明信息与代码(程序集.类型.方法.属性等)相关联.特性与程序实体关联后,可在运行时使用"反射"查询 ...

  4. 区分元素特性attribute和对象属性property

    × 目录 [1]定义 [2]共有 [3]例外[4]特殊[5]自定义[6]混淆[7]总结 前面的话 其实attribute和property两个单词,翻译出来都是属性,但是<javascript高 ...

  5. .Net内置特性Attribute介绍

    特性Attribute概述 特性(Attribute)是一种特殊的类型,可以加载到程序集或者程序集的类型上,这些类型包括模块.类.接口.结构.构造函数.方法.字段等,加载了特性的类型称之为特性的目标. ...

  6. 【点滴积累】通过特性(Attribute)为枚举添加更多的信息

    转:http://www.cnblogs.com/IPrograming/archive/2013/05/26/Enum_DescriptionAttribute.html [点滴积累]通过特性(At ...

  7. 理解特性attribute 和 属性property的区别 及相关DOM操作总结

    查一下英语单词解释,两个都可以表示属性.但attribute倾向于解释为特质,而property倾向于解释私有的.这个property的私有解释可以更方便我们下面的理解. 第一部分:区别点 第一点:  ...

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

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

  9. C# 知识特性 Attribute,XMLSerialize,

    C#知识--获取特性 Attribute 特性提供功能强大的方法,用以将元数据或声明信息与代码(程序集.类型.方法.属性等)相关联.特性与程序实体关联后,可在运行时使用“反射”查询特性,获取特性集合方 ...

  10. c#特性attribute:

    特性是被编译到metadata中,  是提供给反射用的. 特性attribute:1 什么是attribute,和注释有什么区别 2 声明和使用attribute3 使用attribute完成扩展4 ...

随机推荐

  1. CSS基础语法(一) CSS的3种引入

    CSS样式表 CSS可算是网页设计的一个突破,它解决了网页界面排版的难题.可以这么说,HTML的Tag主要是定义网页的内容(Content),而CSS决定这些网页内容如何显示(Layout). Web ...

  2. Hello World, S/4HANA for Customer Management 1.0

    SAP CRM的前世今生 在我之前的微信公众号文章 SAP的这三款CRM解决方案,您能区分清楚么我曾经提到过我作为成都SAP研究院CRM产品开发团队的一员工作过一段时间. 我向在SAP德国总部工作的德 ...

  3. 使用 Android 客户端向 Ruby on rails 构建的 Web Application 提交 HTTP GET 和 HTTP POST 请求

    最近想弄个能访问 Internet 的 Android 应用,因为求快所以用了 Ruby on Rails 来提供 HTTP 资源.这方面的资料还是比较少的,所以把尝试的过程记录下来. 1 使用 Ru ...

  4. echarts图表与可视窗口的自适应

    由于要适应屏幕尺寸,发现了这个问题.网上搜到了两个办法,如下: 方法一: window.onresize = mychart.resize; 方法二: window.addEventListener( ...

  5. HDU 1754 I Hate It 【线段树单点修改 维护区间最大值】

    题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1754 I Hate It Time Limit: 9000/3000 MS (Java/Others ...

  6. 二、IntelliJ IDEA 安装目录的核心文件讲解

    首先,咱们回顾一下前两篇关于 IntelliJ IDEA 的博文的内容: 在“在 Windows 系统下安装 IntelliJ IDEA 的方法”中,咱们知道了在 Windows 系统下如何下载并安装 ...

  7. 【luogu P3376 网络最大流】 模板

    题目链接:https://www.luogu.org/problemnew/show/P3376 #include <iostream> #include <cstdio> # ...

  8. 线段tree~讲解+例题

    最近学习了线段树这一重要的数据结构,有些许感触.所以写一篇博客来解释一下线段树,既是对自己学习成果的检验,也希望可以给刚入门线段树的同学们一点点建议. 首先声明一点,本人是个蒟蒻,如果在博客中有什么不 ...

  9. 【luogu P1816 忠诚】 题解

    题目链接:https://www.luogu.org/problemnew/show/P1816 用st表来解决rmq问题. 表示同时培训学的st表,然后我就忘得差不多了,在这里推荐一篇blog 大佬 ...

  10. opencv 数据类型转换:CvArr, Mat, CvMat, IplImage, BYTE 转

    留着以后查询: http://blog.csdn.net/augusdi/article/details/8863820 一.Mat类型:矩阵类型,Matrix. 在openCV中,Mat是一个多维的 ...