C# 知识回顾 - 特性 Attribute

【博主】反骨仔    【原文地址】http://www.cnblogs.com/liqingwen/p/5911289.html

目录

一、特性简介

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

  特性具有以下属性:

    (1)特性可向程序中添加元数据。元数据是有关在程序中定义的类型的信息。所有的 .NET 程序集都包含指定的一组元数据,这些元数据描述在程序集中定义的类型和类型成员。可以添加自定义特性,以指定所需的任何附加信息。

    (2)可以将一个或多个特性应用到整个程序集、模块或较小的程序元素(如类和属性)。

    (3)特性可以与方法和属性相同的方式接受参数。

    (4)程序可以使用反射检查自己的元数据或其他程序内的元数据。

二、使用特性

  特性可以放置在几乎所有的声明中(但特定的特性可能限制在其上有效的声明类型)。在 C# 中,特性的指定方法为:将括在方括号中的特性名置于其应用到的实体的声明上方。它必须位于所应用于的元素的紧前面并与该元素在同一行。

     [Serializable]  //使用特性 SerializableAttribute
     internal class MyClass
     {
         [DllImport("user32.dll")]   //使用特性 DllImportAttribute
         private static extern void Do();

         #region 一个声明上可放置多个特性

         private void MethodA([In][Out]ref double n) { }
         private void MethodB([In, Out]ref double n) { }

         #endregion 一个声明上可放置多个特性

         #region 某些特性对于给定实体可以指定多次

         [Conditional("DEBUG"), Conditional("TEST1")]
         private void TraceMethod() { }

         #endregion 某些特性对于给定实体可以指定多次
     }

  【注意】根据约定,所有特性名称都以单词“Attribute”结束,以便将它们与“.NET Framework”中的其他项区分。但是,在代码中使用特性时,不需要指定 attribute 后缀。

三、特性的参数

  许多特性都有参数,而这些参数可以是定位参数、未命名参数或命名参数。任何定位参数都必须按特定顺序指定并且不能省略,而命名参数是可选的且可以按任意顺序指定。首先指定定位参数。例如,这三个特性是等效的:

 [DllImport("user32.dll")]
 [DllImport("user32.dll", SetLastError=false, ExactSpelling=false)] 

  第一个参数(DLL 名称)是定位参数并且总是第一个出现,其他参数为命名参数。在这种情况下,两个命名参数均默认为 false,因此可将其省略。

四、特性的目标

  特性的目标是应用该特性的实体。例如,特性可以应用于类、特定方法或整个程序集。默认情况下,特性应用于它后面的元素。但是,您也可以显式标识要将特性应用于方法还是它的参数或返回值。

  若要显式标识特性目标,语法:

[target : attribute-list]
特性目标
C# 适用对象
assembly 整个程序集
module 当前程序集模块
field 在类或结构中的字段
event event
method 方法或 get 和 set 属性访问器
param 方法参数或 set 属性访问器参数
property 属性
return 方法、属性索引器或 get 属性访问器的返回值
type 结构、类、接口、枚举或委托
//示例:将特性应用于程序集和模块
[assembly: AssemblyTitle("assembly 4.6.1")]
[module: CLSCompliant(true)]
 //示例:将特性应用于方法、方法参数和方法返回值

 //默认:应用于方法
 [SomeAttr]
 ; } 

 //指定应用于方法
 [method: SomeAttr]
 ; } 

 //指定应用于返回值
 [return: SomeAttr]
 ; }

五、特性的常见用途

  以下列表包含特性的几个常见用途:

    (1)在 Web 服务中,使用 WebMethod 特性来标记方法,以指示该方法应该可通过 SOAP 协议进行调用。

    (2)描述当与本机代码进行交互操作时如何封送方法参数。有关更多信息。

    (3)描述类、方法和接口的 COM 属性。

    (4)使用 DllImportAttribute 类调用非托管代码。

    (5)在标题、版本、说明或商标方面描述您的程序集。

    (6)描述要持久性序列化类的哪些成员。

    (7)描述如何映射类成员和 XML 节点以便进行 XML 序列化。

    (8)描述方法的安全要求。

    (9)指定用于强制安全性的特性。

    (10)由实时 (JIT) 编译器控制优化,以便易于调试代码。

    (11)获取有关调用方的信息的方法。

六、创建自定义的特性

  通过定义一个特性类,可以创建您自己的自定义特性。该特性类直接或间接地从 Attribute 派生,有助于方便快捷地在元数据中标识特性定义。

     /// <summary>
     /// 角色特性
     /// </summary>
     /// RoleAttribute:特性的名称,继承 Attribute,为自定义特性
     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
     public class RoleAttribute : Attribute
     {
         private string _name;

         /// <summary>
         /// 启用标识
         /// </summary>
         /// IsEnable:命名参数
         public bool IsEnable { get; set; }

         /// <summary>
         /// 构造函数
         /// </summary>
         /// <param name="name"></param>
         /// name:定位参数
         public RoleAttribute(string name)
         {
             _name = name;
         }
     }
     [Role("Me", IsEnable = true)]   //调用特性的方式
     public class OurClass
     {

     }

  构造函数的参数是自定义特性的定位参数,任何公共的读写字段或属性都是命名参数。【注意】 AttributeUsage 特性,在这里它使得 Role 特性仅在类和 struct 声明中有效。

     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]  //AllowMultiple:该值指示能否为一个程序多次使用该特性
     public class RoleAttribute : Attribute
     {
         //... ...
     }
     [Role("You")]            //在同一个类上多次使用
     [Role("Me", IsEnable = true)]
     public class OurClass
     {
         //... ...
     }

  【注意】如果特性类包含一个属性,则该属性必须为读写属性。

七、使用反射访问特性

  使用反射,可检索用自定义特性定义的信息。主要方法是 GetCustomAttributes,它返回对象数组,这些对象在运行时等效于源代码特性。

     /// <summary>
     /// 角色特性
     /// </summary>
     /// RoleAttribute:特性的名称,继承 Attribute,为自定义特性
     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
     public class RoleAttribute : Attribute
     {
         private string _name;
         /// <summary>
         /// 启用标识
         /// </summary>
         public bool IsEnable { get; set; }

         /// <summary>
         /// 构造函数
         /// </summary>
         /// <param name="name"></param>
         public RoleAttribute(string name)
         {
             _name = name;
         }
     }

RoleAttribute.cs

     [Role("Me", IsEnable = true)]
     public class OurClass
     {
         //... ...
     }

  概念上等效于

     RoleAttribute role = new RoleAttribute("Me");
     role.IsEnable = true;

  但是,直到查询 OurClass 来获取特性后才会执行此代码。对 OurClass 调用 GetCustomAttributes 会导致按上述方式构造并初始化一个 RoleAttribute 对象。如果该类具有其他特性,则按相似的方式构造其他特性对象。然后 GetCustomAttributes 返回 RoleAttribute 对象和数组中的任何其他特性对象。之后就可以对此数组进行迭代,确定根据每个数组元素的类型所应用的特性,并从特性对象中提取信息。

  这里,定义一个自定义特性,将其应用于若干实体并通过反射进行检索。

     /// <summary>
     /// 角色特性
     /// </summary>
     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
     public class RoleAttribute : Attribute
     {
         private readonly string _name;

         /// <summary>
         /// 启用标识
         /// </summary>
         public bool IsEnable { get; set; }

         /// <summary>
         /// 构造函数
         /// </summary>
         /// <param name="name"></param>
         public RoleAttribute(string name)
         {
             _name = name;
         }

         public string GetName()
         {
             return _name;
         }
     }
    class MyClass1 { }

    [Role("Me")]
    class MyClass2 { }

    [Role("Me"), Role("You", IsEnable = true)]
    class MyClass3 { }
     class Program
     {
         static void Main(string[] args)
         {
             Output(typeof(MyClass1));
             Output(typeof(MyClass2));
             Output(typeof(MyClass3));

             Console.Read();
         }

         /// <summary>
         /// 输出
         /// </summary>
         /// <param name="t"></param>
         static void Output(Type t)
         {
             Console.WriteLine($"Class: {t}");

             var attributes = t.GetCustomAttributes();
             foreach (var attribute in attributes)
             {
                 var attr = attribute as RoleAttribute;

                 if (attr == null)
                 {
                     return;
                 }

                 Console.WriteLine($"    Name: {attr.GetName()}, IsEnable: {attr.IsEnable}");
             }
         }
     }

传送门

  《只是想简单说下表达式树 - Expression Trees

  《只是想简单说下序列化


【参考】微软官方文档

[C#] C# 知识回顾 - 特性 Attribute的更多相关文章

  1. [C#] C# 知识回顾 - 你真的懂异常(Exception)吗?

    你真的懂异常(Exception)吗? 目录 异常介绍 异常的特点 怎样使用异常 处理异常的 try-catch-finally 捕获异常的 Catch 块 释放资源的 Finally 块 一.异常介 ...

  2. [C#] C# 知识回顾 - 学会处理异常

    学会处理异常 你可以使用 try 块来对你觉得可能会出现异常的代码进行分区. 其中,与之关联的 catch 块可用于处理任何异常情况. 一个包含代码的 finally 块,无论 try 块中是否在运行 ...

  3. [C#] C# 知识回顾 - 学会使用异常

    学会使用异常 在 C# 中,程序中在运行时出现的错误,会不断在程序中进行传播,这种机制称为“异常”. 异常通常由错误的代码引发,并由能够更正错误的代码进行 catch. 异常可由 .NET 的 CLR ...

  4. [C#] C# 知识回顾 - 异常介绍

    异常介绍 我们平时在写程序时,无意中(或技术不够),而导致程序运行时出现意外(或异常),对于这个问题, C# 有专门的异常处理程序. 异常处理所涉及到的关键字有 try.catch 和 finally ...

  5. [.NET] C# 知识回顾 - Event 事件

    C# 知识回顾 - Event 事件 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6060297.html 序 昨天,通过<C# 知识回顾 - ...

  6. [.NET] C# 知识回顾 - 事件入门

    C# 知识回顾 - 事件入门 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6057301.html 序 之前通过<C# 知识回顾 - 委托 de ...

  7. [.NET] C# 知识回顾 - 委托 delegate (续)

    C# 知识回顾 - 委托 delegate (续) [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6046171.html 序 上篇<C# 知识回 ...

  8. [C#] C# 知识回顾 - 委托 delegate

    C# 知识回顾 - 委托 delegate [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6031892.html 目录 What's 委托 委托的属性 ...

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

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

随机推荐

  1. Angular2 组件通信

    1. 组件通信 我们知道Angular2应用程序实际上是有很多父子组价组成的组件树,因此,了解组件之间如何通信,特别是父子组件之间,对编写Angular2应用程序具有十分重要的意义,通常来讲,组件之间 ...

  2. Java MySql 批量插入数据库addBatch

    //addBatch批量插入数据库 public static void insertCommentToMySql(Set<String> commentList) { Iterator& ...

  3. Tomcat6配置webdav协议

    Tomcat6默认是支持webdav协议的,只是webapps目录中不带例子而已.要添加支持很简单,在webapps目录中建立webdav目录,在webdav目录中建立WEB-INF目录,加入web. ...

  4. poj 3295 Tautology

    点击打开链接 Tautology Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8127   Accepted: 3115 ...

  5. tomcat启动

    http://jingyan.baidu.com/article/c33e3f48a42352ea15cbb5d4.html

  6. 【PHP】将EXCEL表中的数据轻松导入Mysql数据表

    在网络上有不较多的方法,在此介绍我已经验证的方法. 方法一.利用EXCEL表本身的功能生成SQL代码 ①.先在“phpmyadmin”中建立数据库与表(数据库:excel,数据表:excel01,字段 ...

  7. 【原创】10万条数据采用存储过程分页实现(Mvc+Dapper+存储过程)

    有时候大数据量进行查询操作的时候,查询速度很大强度上可以影响用户体验,因此自己简单写了一个demo,简单总结记录一下: 技术:Mvc4+Dapper+Dapper扩展+Sqlserver 目前主要实现 ...

  8. 【Qt编程】3D迷宫游戏

    说起迷宫想必大家都很熟悉,个人感觉迷宫对人的方向感是很大的考验,至少我的方向感是不好的,尤其是在三维空间中.由于这段时间帮导师做项目用到了三维作图,便心血来潮想做个三维迷宫玩玩.要想画出三维的迷宫游戏 ...

  9. Luogu P3600 随机数生成器(期望+dp)

    题意 有一个长度为 \(n\) 的整数列 \(a_1, a_2, \cdots, a_n\) ,每个元素在 \([1, x]\) 中的整数中均匀随机生成. 有 \(q\) 个询问,第 \(i\) 个询 ...

  10. ssh到虚拟机---一台主机上

    问题描述:我们需要ssh来编辑虚拟机中的文件,以此提高工作效率.但是新建的虚机一般来说没有开启ssh服务,所以需要在虚拟机上开启ssh服务. 1)检查是否安装了SSH rpm -qa |grep ss ...