特性 Attribute

 

目录

一、特性简介

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

  特性具有以下属性:

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

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

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

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

二、使用特性

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

复制代码
 1     [Serializable]  //使用特性 SerializableAttribute
 2     internal class MyClass
 3     {
 4         [DllImport("user32.dll")]   //使用特性 DllImportAttribute
 5         private static extern void Do();
 6 
 7         #region 一个声明上可放置多个特性
 8 
 9         private void MethodA([In][Out]ref double n) { }
10         private void MethodB([In, Out]ref double n) { }
11 
12         #endregion 一个声明上可放置多个特性
13 
14         #region 某些特性对于给定实体可以指定多次
15 
16         [Conditional("DEBUG"), Conditional("TEST1")]
17         private void TraceMethod() { }
18 
19         #endregion 某些特性对于给定实体可以指定多次
20     }
复制代码

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

三、特性的参数

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

1 [DllImport("user32.dll")] 
2 [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)]
复制代码
 1 //示例:将特性应用于方法、方法参数和方法返回值
 2 
 3 //默认:应用于方法
 4 [SomeAttr] 
 5 int Method1() { return 0; } 
 6 
 7 //指定应用于方法
 8 [method: SomeAttr]
 9 int Method2() { return 0; } 
10 
11 //指定应用于返回值
12 [return: SomeAttr] 
13 int Method3() { return 0; }
复制代码

五、特性的常见用途

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

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

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

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

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

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

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

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

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

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

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

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

 

六、创建自定义的特性

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

复制代码
 1     /// <summary>
 2     /// 角色特性
 3     /// </summary>
 4     /// RoleAttribute:特性的名称,继承 Attribute,为自定义特性
 5     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
 6     public class RoleAttribute : Attribute
 7     {
 8         private string _name;
 9 
10         /// <summary>
11         /// 启用标识
12         /// </summary>
13         /// IsEnable:命名参数
14         public bool IsEnable { get; set; }
15 
16         /// <summary>
17         /// 构造函数
18         /// </summary>
19         /// <param name="name"></param>
20         /// name:定位参数
21         public RoleAttribute(string name)
22         {
23             _name = name;
24         }
25     }
复制代码
1     [Role("Me", IsEnable = true)]   //调用特性的方式
2     public class OurClass
3     {
4 
5     }

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

 

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

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

 

七、使用反射访问特性

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

 1     /// <summary>
 2     /// 角色特性
 3     /// </summary>
 4     /// RoleAttribute:特性的名称,继承 Attribute,为自定义特性
 5     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
 6     public class RoleAttribute : Attribute
 7     {
 8         private string _name;
 9         /// <summary>
10         /// 启用标识
11         /// </summary>
12         public bool IsEnable { get; set; }
13 
14         /// <summary>
15         /// 构造函数
16         /// </summary>
17         /// <param name="name"></param>
18         public RoleAttribute(string name)
19         {
20             _name = name;
21         }
22     }
RoleAttribute.cs
1     [Role("Me", IsEnable = true)]   
2     public class OurClass
3     {
4         //... ...
5     }

  概念上等效于

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

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

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

复制代码
 1     /// <summary>
 2     /// 角色特性
 3     /// </summary>
 4     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
 5     public class RoleAttribute : Attribute
 6     {
 7         private readonly string _name;
 8 
 9         /// <summary>
10         /// 启用标识
11         /// </summary>
12         public bool IsEnable { get; set; }
13 
14         /// <summary>
15         /// 构造函数
16         /// </summary>
17         /// <param name="name"></param>
18         public RoleAttribute(string name)
19         {
20             _name = name;
21         }
22 
23         public string GetName()
24         {
25             return _name;
26         }
27     }
复制代码
复制代码
复制代码
    class MyClass1 { }

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

    [Role("Me"), Role("You", IsEnable = true)]
    class MyClass3 { }
复制代码
 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Output(typeof(MyClass1));
 6             Output(typeof(MyClass2));
 7             Output(typeof(MyClass3));
 8 
 9             Console.Read();
10         }
11 
12         /// <summary>
13         /// 输出
14         /// </summary>
15         /// <param name="t"></param>
16         static void Output(Type t)
17         {
18             Console.WriteLine($"Class: {t}");
19 
20             var attributes = t.GetCustomAttributes();
21             foreach (var attribute in attributes)
22             {
23                 var attr = attribute as RoleAttribute;
24 
25                 if (attr == null)
26                 {
27                     return;
28                 }
29 
30                 Console.WriteLine($"    Name: {attr.GetName()}, IsEnable: {attr.IsEnable}");
31             }
32         }
33     }
复制代码

 

 

 

 

 

 

************转摘:https://www.cnblogs.com/liqingwen/p/5911289.html

 
原文地址:https://www.cnblogs.com/linybo/p/13218436.html