C# 特性詳解


  特性提供功能強大的方法,用以將元數據或聲明信息與代碼(程序集、類型、方法、屬性等)相關聯。這些元數據是在編譯過程中創建,並嵌入到程序集中。特性與程序實體關聯后,即可在運行時使用名為“反射”的技術查詢特性。反射是個普通術語,它描述了在運行過程中檢查和處理程序元素的功能。

特性具有以下屬性:

  • 特性可向程序中添加元數據。元數據是有關在程序中定義的類型的信息。所有的 .NET 程序集都包含指定的一組元數據,這些元數據描述在程序集中定義的類型和類型成員。可以添加自定義特性,以指定所需的任何附加信息。

  • 可以將一個或多個特性應用到整個程序集、模塊或較小的程序元素(如類和屬性)。

  • 特性可以與方法和屬性相同的方式接受參數。

  • 程序可以使用反射檢查自己的元數據或其他程序內的元數據。

元數據是什么?

  你注意過程序及編譯的時候的pdb文件了嗎?pdb文件里面存儲了,關於程序集內部的所有的成員信息,例如,成員的數據類型,屬性類型,方法返回值,方法入參類型,就是程序及內部所有的定義信息的數據信息,是存儲定義信息的一類數據信息,程序集里面的所有的關於聲明類的數據信息,包括方法間調用,都是存儲在元數據里面。

詳細查看:http://www.cnblogs.com/DswCnblog/p/5344119.html

 

特性可以放置在幾乎所有的聲明中。在 C# 中,特性的指定方法為:將括在方括號中的特性名置於其應用到的實體的聲明上方,一個元素上面可以放置多個特性

[System.Serializable]
public class SampleClass
{
    // Objects of this type can be serialized.
}

  根據約定,所有特性名稱都以單詞“Attribute”結束,以便將它們與“.NET Framework”中的其他項區分。在代碼中使用特性,C#編譯器會把字符串Attribute追加到這個特性名稱后面,然后在其搜索路徑的所有名稱空間中搜索指定名稱的類。如果該特性的名稱已字符串Attribute結尾,編譯器就不會把字符串加到組合名稱中。

預定義特性(Attribute)

.Net 框架提供了三種預定義特性:

  • AttributeUsage
  • Conditional
  • Obsolete

AttributeUsage特性

預定義特性 AttributeUsage 主要用於標示自定義特性可以應用到哪些類型的程序元素上,這個信息由第一個參數給出。

規定該特性的語法如下:

[AttributeUsage(
     validon,               //規定特性可被放置的語言元素,它是枚舉器 AttributeTargets 的值的組合,默認值是 AttributeTargets.All
     AllowMultiple=allowmultiple,   //如果為 true,則該特性可以在同一個元素多次使用,默認值是 false(不可多次使用)
     Inherited=inherited        //如果為 true,則該特性可被派生類繼承,默認值是 false(不被繼承)
)]

例如:

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

 AttributeTargets :

  成員名稱 說明
  Assembly 可以對程序集應用特性。
  Module 可以對模塊應用特性。
注意注意
Module 指的是可移植的可執行文件(.dll 或 .exe),而非 Visual Basic 標准模塊。
  Class 可以對類應用特性。
  Struct 可以對結構應用特性,即值類型。
  Enum 可以對枚舉應用特性。
  Constructor 可以對構造函數應用特性。
  Method 可以對方法應用特性。
  Property 可以對屬性應用特性。
  Field 可以對字段應用特性。
  Event 可以對事件應用特性。
  Interface 可以對接口應用特性。
  Parameter 可以對參數應用特性。
  Delegate 可以對委托應用特性。
  ReturnValue 可以對返回值應用特性。
  GenericParameter 可以對泛型參數應用特性。
注意注意
目前,此特性可以應用僅於 C#、Microsoft 中間語言 (MSIL) 和發出的代碼。
  All 可以對任何應用程序元素應用特性。

 按照上面的經驗,再次開始動手來熟悉這一切,我指定了該自定義的特性不可繼承,就在不解釋別的了只是為了證明一下命名參數Inherited定性成功與否,總之還是很簡單的。

namespace
{
    class Program
    {
        static void Main(string[] args)
        {
            GetAttributeInfo(typeof(OldClass));
            Console.WriteLine("==============");
            GetAttributeInfo(typeof(NewClass));
            Console.ReadKey();
        }
        public static void GetAttributeInfo(Type t)
        {
            OldAttribute myattribute = (OldAttribute)Attribute.GetCustomAttribute(t, typeof(OldAttribute));
            if (myattribute == null)
            {
                Console.WriteLine(t.ToString()+"類中自定義特性不存在!");
            }
            else
            {
                Console.WriteLine("特性描述:{0}\n加入事件{1}", myattribute.Discretion, myattribute.date);
            }
        }
    }
 
   [AttributeUsage(AttributeTargets.Class,Inherited=false)]  //設置了定位參數和命名參數
    class OldAttribute : Attribute    //繼承自Attribute
    {
        private string discretion;
 
        public string Discretion
        {
            get { return discretion; }
            set { discretion = value; }
        }
public DateTime date; public OldAttribute(string discretion) { this.discretion = discretion; date = DateTime.Now; } }
//現在我們定義兩類 [Old("這個類將過期")]//使用定義的新特性 class OldClass { public void OldTest() { Console.WriteLine("測試特性"); } }
class NewClass:OldClass { public void NewTest() { Console.WriteLine("測試特性的繼承"); } } //我們寫一個方法用來獲取特性信息 }

運行效果:

Conditional

這個預定義特性標記了一個條件方法,其執行依賴於它頂的預處理標識符。

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

#define DEBUG        //定義DEBUG宏,則下面有輸出,不定義則沒有輸出
using System;
using System.Diagnostics;
public class Myclass
{
    [Conditional("DEBUG")]
    public static void Message(string msg)
    {
        Console.WriteLine(msg);
    }
}
class Test { static void function1() { Myclass.Message("In Function 1."); function2(); } static void function2() { Myclass.Message("In Function 2."); } public static void Main() { Myclass.Message("In Main function."); function1(); Console.ReadKey(); } }

ObsoleteAttribute

  這個預定義特性標記了不應被使用的程序實體。它可以讓您通知編譯器丟棄某個特定的目標元素。例如,當一個新方法被用在一個類中,但是您仍然想要保持類中的舊方法,您可以通過顯示一個應該使用新方法,而不是舊方法的消息,來把它標記為 obsolete(過時的)。

public ObsoleteAttribute(string message, bool error)

  參數             類型:

  message       System ..::.String        描述可選的變通方法的文本字符串。

 error          System ..::.Boolean       指示是否將使用已過時的元素視為錯誤的布爾值。

using System;
public class MyClass
{
   [Obsolete("Don't use OldMethod, use NewMethod instead", true)]
   static void OldMethod()
   { 
      Console.WriteLine("It is the old method");
   }
public static void Main()
   {
      OldMethod();      //將會報錯
   }
}

自定義特性

  通過定義一個特性類,該特性類直接或間接地從Attribute派生,有助於方便快捷地在元數據中標識特性定義。特性類本身用一個特性-System.AttributeUsage特性來標記,這個是Microsoft定義的特性,AttributeUsage主要用於表示自定義特性可以應用到哪些類型的程序上。這些信息由它的第一個參數給出,該參數是必選的,其類型是枚舉類型AttributeTarget。

一個新的自定義特性應派生自 System.Attribute 類,例如:
// 一個自定義特性 BugFix 被賦給類及其成員
[AttributeUsage(AttributeTargets.Class |
  AttributeTargets.Constructor |
  AttributeTargets.Field |
  AttributeTargets.Method |
  AttributeTargets.Property,
  AllowMultiple = true)]

public class DeBugInfo : System.Attribute    //已經聲明了一個名為 DeBugInfo 的自定義特性。

讓我們構建一個名為 DeBugInfo 的自定義特性,該特性將存儲調試程序獲得的信息。它存儲下面的信息:

  • bug 的代碼編號
  • 辨認該 bug 的開發人員名字
  • 最后一次審查該代碼的日期
  • 一個存儲了開發人員標記的字符串消息

我們的 DeBugInfo 類將帶有三個用於存儲前三個信息的私有屬性(property)和一個用於存儲消息的公有屬性(property)。所以 bug 編號、開發人員名字和審查日期將是 DeBugInfo 類的必需的定位( positional)參數,消息將是一個可選的命名(named)參數。

每個特性必須至少有一個構造函數。必需的定位( positional)參數應通過構造函數傳遞。下面的代碼演示了 DeBugInfo 類:

// 一個自定義特性 BugFix 被賦給類及其成員
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]

public class DeBugInfo : System.Attribute
{
  private int bugNo;
  private string developer;
  private string lastReview;
  public string message;

  public DeBugInfo(int bg, string dev, string d)
  {
      this.bugNo = bg;
      this.developer = dev;
      this.lastReview = d;
  }

  public int BugNo
  {
      get
      {
          return bugNo;
      }
  }
  public string Developer
  {
      get
      {
          return developer;
      }
  }
  public string LastReview
  {
      get
      {
          return lastReview;
      }
  }
  public string Message
  {
      get
      {
          return message;
      }
      set
      {
          message = value;
      }
  }
}

應用自定義特性

通過把特性放置在緊接着它的目標之前,來應用該特性:

[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle
{
  // 成員變量
  protected double length;
  protected double width;
  public Rectangle(double l, double w)
  {
      length = l;
      width = w;
  }
  [DeBugInfo(55, "Zara Ali", "19/10/2012",
  Message = "Return type mismatch")]
  public double GetArea()
  {
      return length * width;
  }
  [DeBugInfo(56, "Zara Ali", "19/10/2012")]
  public void Display()
  {
      Console.WriteLine("Length: {0}", length);
      Console.WriteLine("Width: {0}", width);
      Console.WriteLine("Area: {0}", GetArea());
  }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM