天天看點

C#-特性(Attribute)

概念

特性是一種允許我們向程式的程式集添加中繼資料的語言結構,它是用于儲存程式結構資訊的某種特殊類型的類。

可以通過使用特性向程式添加聲明性資訊。一個聲明性标簽是通過放置在它所應用的元素前面的方括号([ ])來描述的。

MSDN解釋為:特性提供功能強大的方法,用以将中繼資料或聲明資訊與代碼(程式集、類型、方法、屬性等)相關聯。特性與程式實體關聯後,即可在運作時使用名為“反射”的技術查詢特性。

特性(Attribute)用于添加中繼資料,如編譯器指令和注釋、描述、方法、類等其他資訊。.Net 架構提供了兩種類型的特性:預定義特性和自定義特性。

特性具有以下屬性:

  • 特性可向程式中添加中繼資料。中繼資料是有關在程式中定義的類型的資訊。所有的 .NET 程式集都包含指定的一組中繼資料,這些中繼資料描述在程式集中定義的類型和類型成員。可以添加自定義特性,以指定所需的任何附加資訊。
  • 可以将一個或多個特性應用到整個程式集、子產品或較小的程式元素(如類和屬性)。
  • 特性可以與方法和屬性相同的方式接受參數。
  • 程式可以使用反射檢查自己的中繼資料或其他程式内的中繼資料。

使用特性

特性的目的是告訴編譯器把程式結構的某組中繼資料嵌入程式集,它可以放置在幾乎所有的聲明中(但特定的屬性可能限制在其上有效的聲明類型)。

在 C# 中,特性的指定方法為:将括在方括号中的特性名置于其應用到的實體的聲明上方。

例如:

[System.Serializable]
public class SampleClass
{
    // 運作語句
}      

一個聲明中可以放置多個特性,有兩種寫法。第一種是将獨立的特性片段互相疊在一起,第二種是使用一個特性片段,特性之間使用逗号分割。

[System.Serializable]
[MyAttribute("par1","par2")]  // 獨立的特性片段
public class SampleClass
{
    // 運作語句
}

[MyAttribute("par1","par2"), System.Serializable]  // 逗号分割使用
public class SampleClass
{
    // 運作語句
}      

對于某些特性可以對給定實體進行指定多次。例如 ConditionalAttribute :

[Conditional("DEBUG"), Conditional("TEST1")]
void TraceMethod()
{
    // 運作語句
}
      

特性目标

特性的目标是應用該特性的實體。例如,特性可以應用于類、特定方法或整個程式集。預設情況下,特性應用于它後面的元素。但是,您也可以顯式辨別要将特性應用于方法還是它的參數或傳回值。

文法:

[target : attribute-list]      
C#-特性(Attribute)

預定義特性

.Net 架構提供了三種預定義特性:AttributeUsage、Conditional、Obsolete。

AttributeUsage

AttributeUsage 描述了如何使用一個自定義特性類。它規定了特性可應用到的項目的類型。

[AttributeUsage(
   validon,
   AllowMultiple=allowmultiple,
   Inherited=inherited
)]
      

參數說明:

  • validon: 規定特性可被放置的語言元素。它是枚舉器 AttributeTargets 的值的組合。預設值是 AttributeTargets.All。
  • allowmultiple:可選的參數,為該特性的 AllowMultiple 屬性(property)提供一個布爾值。如果為 true,則該特性是多用的。預設值是 false(單用的)。
  • inherited:可選的參數,為該特性的 Inherited 屬性(property)提供一個布爾值。如果為 true,則該特性可被派生類繼承。預設值是 false(不被繼承)。

示例:

[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property, 
AllowMultiple = true)]      

這個預定義特性标記了一個條件方法,其執行依賴于它頂的預處理辨別符。

它會引起方法調用的條件編譯,取決于指定的值,比如 Debug 或 Trace。例如,當調試代碼時顯示變量的值。

文法:

[Conditional(
   conditionalSymbol
)]
      
using System;
using System.Diagnostics;

namespace C_Pro
{
    public class Myclass
    {
        [Conditional("DEBUG")]
        public static void Msg(string msg)
        {
            Console.WriteLine(msg);
        }
    }
    public class Test
    {
        static void Main()
        {
            Myclass.Msg("debug in Main.");
            Console.WriteLine("Done.");
            Console.ReadKey();
        }
    }
}      

以debug模式進行運作:

debug in Main.
Done.      

Obsolete

這個預定義特性标記了不應被使用的程式實體。它可以讓您通知編譯器丢棄某個特定的目标元素。

[Obsolete(
   message,
   iserror
)]        
  • message:是一個字元串,描述項目為什麼過時的原因以及該替代使用什麼。
  • iserror:是一個布爾值。如果該值為 true,編譯器應把該項目的使用當作一個錯誤。預設值是 false(編譯器生成一個警告)。
using System;

namespace C_Pro
{

    public class MyClass
    {
        [Obsolete("Don't use OldMethod, use NewMethod instead", true)]
        static void OldMethod()
        {
            Console.WriteLine("It is the old method");
        }
         [Obsolete("Don't use OldMethod, use NewMethod instead", false)]
        static void NewMethod()
        {
            Console.WriteLine("It is the new method");
        }
        public static void Main()
        {
            OldMethod();
            NewMethod();
            Console.ReadKey();
        }

    }
}      

運作後結果:

Main.cs(20,4): error CS0619: `C_Pro.MyClass.OldMethod()' is obsolete: `Don't use OldMethod, use NewMethod instead'
Main.cs(21,4): warning CS0618: `C_Pro.MyClass.NewMethod()' is obsolete: `Don't use OldMethod, use NewMethod instead'
Compilation failed: 1 error(s), 1 warnings      

如果在 VS 中可以直接看到 OldMethod() 會報錯提示:

C#-特性(Attribute)

自定義特性

聲明特性和聲明其他類是一樣的,隻是所有的特性都派生自System.Attribute。

根據約定,所有特性名稱都以單詞“Attribute”結束,以便将它們與“.NET Framework”中的其他項區分。但是,在代碼中使用特性時,不需要指定 attribute 字尾。

例如定義一個MyAttributeAttribute,由 System.Attribute 派生而來,是以是自定義特性類。

public class MyAttributeAttribute : System.Attribute
{
        public string product;
        public string Description;

        public MyAttributeAttribute(string product)
        {
            this.product = product;
            Description = "";
        }
}      

特性也有構造函數。和其他類一樣,每個特性至少有一個公共構造函數,如果沒有聲明構造函數,編譯器則會産生一個隐式、公共且無參的構造函數。

構造函數的參數是自定義特性的定位參數。例如上面的示例中 product 是定位參數,Description 是命名參數。任何公共的讀寫字段或屬性都是命名參數。

定義完成後使用特性:

[Author("P. Pro", Description = "Description")]
class SampleClass
{
    // 執行語句
}      

特性與注釋的差別

注釋是對程式源代碼的一種說明,主要目的是給人看的,在程式被編譯的時候會被編譯器所丢棄,是以,它絲毫不會影響到程式的執行。

Attribute是程式代碼的一部分,它不但不會被編譯器丢棄,而且還會被編譯器編譯程序式集(Assembly)的中繼資料(Metadata)裡。在程式運作的時候,随時可以從中繼資料(中繼資料:.NET中中繼資料是指程式集中的命名空間、類、方法、屬性等資訊,這些資訊是可以通過Reflection讀取出來的。)中提取提取出這些附加資訊,并以之決策程式的運作。