1、什么是Atrribute
首先,我们肯定Attribute是一个类,下面是msdn文档对它的描述:
公共语言运行时允许你添加类似关键字的描述声明,叫做attributes, 它对程序中的元素进行标注,如类型、字段、方法和属性等。Attributes和Microsoft .NET Framework文件的元数据保存在一起,可以用来向运行时描述你的代码,或者在程序运行的时候影响应用程序的行为。

在.NET中,Attribute被用来处理多种问题,比如序列化、程序的安全特征、防止即时编译器对程序代码进行优化从而代码容易调试等等。下面,我们先来看几个在.NET中标准的属性的使用,稍后我们再回过头来讨论Attribute这个类本身。(文中的代码使用C#编写,但同样适用所有基于.NET的所有语言)

2、Attribute作为编译器的指令
在C#中存在着一定数量的编译器指令,如:#define DEBUG, #undefine DEBUG, #if等。这些指令专属于C#,而且在数量上是固定的。而Attribute用作编译器指令则不受数量限制。比如下面的三个Attribute:

Conditional:起条件编译的作用,只有满足条件,才允许编译器对它的代码进行编译。一般在程序调试的时候使用。
   DllImport:用来标记非.NET的函数,表明该方法在一个外部的DLL中定义。
   Obsolete:这个属性用来标记当前的方法已经被废弃,不再使用了。
下面的代码演示了上述三个属性的使用:


 1#define DEBUG //这里定义条件
 2    
 3using System;
 4using System.Runtime.InteropServices;
 5using System.Diagnostics;
 6    
 7namespace AttributeDemo
 8{
 9   class MainProgramClass
   {
 
      [DllImport("User32.dll")]
      public static extern int MessageBox(int hParent, string Message, string Caption, int Type);
     
      static void Main(string[] args)
      {
         DisplayRunningMessage();
         DisplayDebugMessage();
     
         MessageBox(,"Hello","Message",);
     
         Console.ReadLine();
      }
     
      [Conditional("DEBUG")]
      private static void DisplayRunningMessage()
      {
         Console.WriteLine("开始运行Main子程序。当前时间是"+DateTime.Now);
      }
  
      [Conditional("DEBUG")]
      //[Obsolete("Don't use Old method, use New method", true)] 
      [Obsolete]
      private static void DisplayDebugMessage()
      {
         Console.WriteLine("开始Main子程序");
      }
   }
}  

如果在一个程序元素前面声明一个Attribute,那么就表示这个Attribute被施加到该元素上,前面的代码,[DllImport]施加到MessageBox函数上, [Conditional]施加到DisplayRuntimeMessage方法和DisplayDebugMessage方法,[Obsolete]施加到DisplayDebugMessage方法上。

根据上面涉及到的三个Attribute的说明,我们可以猜到程序运行的时候产生的输出:DllImport Attribute表明了MessageBox是User32.DLL中的函数,这样我们就可以像内部方法一样调用这个函数。

重要的一点就是Attribute就是一个类,所以DllImport也是一个类,Attribute类是在编译的时候被实例化的,而不是像通常的类那样在运行时候才实例化。Attribute实例化的时候根据该Attribute类的设计可以带参数,也可以不带参数,比如DllImport就带有"User32.dll"的参数。Conditional对满足参数的定义条件的代码进行编译,如果没有定义DEBUG,那么该方法将不被编译,读者可以把#define DEBUG一行注释掉看看输出的结果(release版本,在Debug版本中Conditional的debug总是成立的)。Obsolete表明了DispalyDebugMessage方法已经过时了,它有一个更好的方法来代替它,当我们的程序调用一个声明了Obsolete的方法时,那么编译器会给出信息,Obsolete还有其他两个重载的版本。大家可以参考msdn中关于的ObsoleteAttribute 类的描述。

3、Attribute类
除了.NET提供的那些Attribute派生类之外,我们可以自定义我们自己的Attribute,所有自定义的Attribute必须从Attribute类派生。现在我们来看一下Attribute 类的细节:

protected Attribute(): 保护的构造器,只能被Attribute的派生类调用。

三个静态方法:

static Attribute GetCustomAttribute():这个方法有8种重载的版本,它被用来取出施加在类成员上指定类型的Attribute。

static Attribute[] GetCustomAttributes(): 这个方法有16种重载版本,用来取出施加在类成员上指定类型的Attribute数组。

static bool IsDefined():由八种重载版本,看是否指定类型的定制attribute被施加到类的成员上面。

实例方法:

bool IsDefaultAttribute(): 如果Attribute的值是默认的值,那么返回true。

bool Match():表明这个Attribute实例是否等于一个指定的对象。

公共属性: TypeId: 得到一个唯一的标识,这个标识被用来区分同一个Attribute的不同实例。

我们简单地介绍了Attribute类的方法和属性,还有一些是从object继承来的。这里就不列出来了。

下面介绍如何自定义一个Attribute: 自定义一个Attribute并不需要特别的知识,其实就和编写一个类差不多。自定义的Attribute必须直接或者间接地从Attribute这个类派生,如:

public MyCustomAttribute : Attribute { ... }

这里需要指出的是Attribute的命名规范,也就是你的Attribute的类名+"Attribute",当你的Attribute施加到一个程序的元素上的时候,编译器先查找你的Attribute的定义,如果没有找到,那么它就会查找“Attribute名称"+Attribute的定义。如果都没有找到,那么编译器就报错。

4、定义或控制特性的使用

对于一个自定义的Attribute,你可以通过AttributeUsage的Attribute来限定你的Attribute 所施加的元素的类型。代码形式如下:
[AttriubteUsage(参数设置)] public 自定义Attribute : Attribute { ... }

非常有意思的是,AttributeUsage本身也是一个Attribute,这是专门施加在Attribute类的Attribute. AttributeUsage自然也是从Attribute派生,它有一个带参数的构造器,这个参数是AttributeTargets的枚举类型。下面是AttributeTargets 的定义:


 1public enum AttributeTargets
 2{
 3   All=,
 4   Assembly=,
 5   Module=,
 6   Class=,
 7   Struct=,
 8   Enum=,
 9   Constructor=,
   Method=,
   Property=,
   Field=,
   Event=,
   Interface=,
   Parameter=,
   Delegate=,
   ReturnValue=
}     

作为参数的AttributeTarges的值允许通过“或”操作来进行多个值得组合,如果你没有指定参数,那么默认参数就是All 。 AttributeUsage除了继承Attribute 的方法和属性之外,还定义了以下三个属性:

AllowMultiple: 读取或者设置这个属性,表示是否可以对一个程序元素施加多个Attribute 。

Inherited:读取或者设置这个属性,表示是否施加的Attribute 可以被派生类继承或者重载。

ValidOn: 读取或者设置这个属性,指明Attribute 可以被施加的元素的类型。

下面让我们来做一些实际的东西。我们将会在Help特性前放置AttributeUsage特性以期待在它的帮助下控制Help特性的使用。


 1using System; 
 2[AttributeUsage(AttributeTargets.Class), AllowMultiple = false, 
 3Inherited = false ] 
 4public class HelpAttribute : Attribute 
 5{ 
 6public HelpAttribute(String Description_in) 
 7{ 
 8this.description = Description_in; 
 9} 
protected String description; 
public String Description 

get 

return this.description; 


}

  先让我们来看一下AttributeTargets.Class。它规定了Help特性只能被放在class的前面。这也就意味着下面的代码将会产生错误:


[Help("this is a do-nothing class")] 
public class AnyClass 

[Help("this is a do-nothing method")] //error 
public void AnyMethod() 


}

  编译器报告错误如下:

  AnyClass.cs: Attribute 'Help' is not valid on this declaration type.

  It is valid on 'class' declarations only.

  我们可以使用AttributeTargets.All来允许Help特性被放置在任何程序实体前。可能的值是:

  Assembly,Module,Class,Struct,Enum,Constructor,Method,Property,Field,Event,Interface,
Parameter,Delegate。

  All = Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | Event | Interface | Parameter | Delegate,

  ClassMembers = Class | Struct | Enum | Constructor | Method | Property | Field | Event | Delegate | Interface )

  下面考虑一下AllowMultiple = false。它规定了特性不能被重复放置多次。


[Help("this is a do-nothing class")] 
[Help("it contains a do-nothing method")] 
public class AnyClass 

[Help("this is a do-nothing method")] //error 
public void AnyMethod() 


}

  它产生了一个编译期错误。

  AnyClass.cs: Duplicate 'Help' attribute

  Ok,现在我们来讨论一下最后的这个属性。Inherited, 表明当特性被放置在一个基类上时,它能否被派生类所继承。


[Help("BaseClass")] 
public class Base 

}  public class Derive : Base 

}

  这里会有四种可能的组合:

  [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false ]

  [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false ]

  [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true ]

  [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true ]

  第一种情况:

  如果我们查询(Query)(稍后我们会看到如何在运行期查询一个类的特性)Derive类,我们将会发现Help特性并不存在,因为inherited属性被设置为false。

  第二种情况:

  和第一种情况相同,因为inherited也被设置为false。

  第三种情况:

  为了解释第三种和第四种情况,我们先来给派生类添加点代码:


[Help("BaseClass")] 
public class Base 


[Help("DeriveClass")] 
public class Derive : Base 

}

  现在我们来查询一下Help特性,我们只能得到派生类的属性,因为inherited被设置为true,但是AllowMultiple却被设置为false。因此基类的Help特性被派生类Help特性覆盖了。

  第四种情况:

  在这里,我们将会发现派生类既有基类的Help特性,也有自己的Help特性,因为AllowMultiple被设置为true。

至此,我们介绍了有关Attribute类和它们的代码格式。你一定想知道到底如何在你的应用程序中使用Attribute,如果仅仅是前面介绍的内容,还是不足以说明Attribute有什么实用价值的话,那么从后面的章节开始我们将介绍几个Attribute的不同用法,相信你一定会对Attribute有一个新的了解。

C# 特性(Attribute)详细介绍的更多相关文章

  1. Apache Spark 2.2.0 新特性详细介绍

    本章内容: 待整理 参考文献: Apache Spark 2.2.0新特性详细介绍 Introducing Apache Spark 2.2

  2. .Net内置特性Attribute介绍

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

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

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

  4. kvm详细介绍

    KVM详解,太详细太深入了,经典 2016-07-18 19:56:38 分类: 虚拟化 原文地址:KVM详解,太详细太深入了,经典 作者:zzjlzx KVM 介绍(1):简介及安装 http:// ...

  5. C# 自定义特性(Attribute)详解

    什么是特性 特性的定义:公共语言运行时允许添加类似关键字的描述声明,叫做attribute,它对程序中的元素进行标注,如类型.字段.方法.和属性等.attribute和.NetFramework文件的 ...

  6. php CGI、Fastcgi、PHP-FPM的详细介绍与之间的关系

    以下PHP CGI.Fastcgi.PHP-FPM的一些信息归纳和汇总----->详细介绍与之间的关系 一:CGI是干嘛的?CGI是为了保证web server传递过来的数据是标准格式的 web ...

  7. 【转载】硬盘MBR详细介绍

    原文地址:http://blog.chinaunix.net/uid-15007890-id-106892.html 硬盘MBR详细介绍      硬盘是现在计算机上最常用的存储器之一.我们都知道,计 ...

  8. Java 集合系列07之 Stack详细介绍(源码解析)和使用示例

    概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它. ...

  9. Java 集合系列12之 TreeMap详细介绍(源码解析)和使用示例

    概要 这一章,我们对TreeMap进行学习.我们先对TreeMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用TreeMap.内容包括:第1部分 TreeMap介绍第2部分 TreeMa ...

  10. Android manifest之manifest标签详细介绍

    AndroidManifest详细介绍 本文主要对AndroidManifest.xml文件中各个标签进行说明.索引如下: 概要PART--01 manifest标签PART--02 安全机制和per ...

随机推荐

  1. 群里提到的IE设置问题 ---B/S 下页面刷新问题

    这里面四个选项的含义 下面是每个选项的作用和意义: 1. “每次访问此页时检查”选项表示浏览器每次访问一个页面时,不管浏览器是否缓存过此页面,都要向服务器发出访问请求.这种设置的优点是实时性很强,肯定 ...

  2. OneZero第四周第三次站立会议(2016.4.13)

    1. 时间: 15:15--15:30  共计15分钟. 2. 成员: X 夏一鸣 * 组长 (博客:http://www.cnblogs.com/xiaym896/), G 郭又铭 (博客:http ...

  3. Python3 - DBUtils 和 pymysql 整合

    之前一篇Python 封装DBUtils 和pymysql 中写过一个basedao.py,最近几天又重新整理了下思绪,优化了下 basedao.py,目前支持的方法还不多,后续会进行改进.添加. 主 ...

  4. 索引器 C#

    概述 索引器允许类或结构的实例就像数组一样进行索引. 索引器类似于属性,不同之处在于它们的访问器采用参数. 在下面的示例中,定义了一个泛型类,并为其提供了简单的 get 和 set 访问器方法(作为分 ...

  5. vagrant 入门

    0.说明 本门所有的操作都是基于mac.windows可做相应调整,大体流程是一样的. 1.什么是vagrant 通俗来讲vagrant是一个通过命令行,来管理虚拟机的工具,vagrant本身并没有提 ...

  6. 【Java并发编程】之十五:并发编程中实现内存可见的两种方法比较:加锁和volatile变量

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17290021 在http://blog.csdn.net/ns_code/article/ ...

  7. Luogu4783 【模板】矩阵求逆(高斯消元)

    对矩阵进行高斯消元直至消为单位矩阵,并在另一个单位矩阵上对其做同样的操作即可. 模意义下的高斯消元可以直接计算系数来避免整行的辗转相除. 还不知道有什么用. #include<iostream& ...

  8. spring MVC 统一异常处理(webapi和web分开处理)

    转载: http://blog.csdn.net/m13321169565/article/details/7641978 http://blog.csdn.net/ethan_fu/article/ ...

  9. 自动化运维—Ansible(上)

    一:为什么选择Ansible 相对于puppet和saltstack,ansible无需客户端,更轻量级 ansible甚至都不用启动服务,仅仅只是一个工具,可以很轻松的实现分布式扩展 更强的远程命令 ...

  10. 【刷题】LOJ 6014 「网络流 24 题」最长 k 可重区间集

    题目描述 给定实直线 \(L\) 上 \(n\) 个开区间组成的集合 \(I\) ,和一个正整数 \(k\) ,试设计一个算法,从开区间集合 \(I\) 中选取出开区间集合 \(S \subseteq ...