程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> .NET實例教程 >> 第三回:歷史糾葛:特性和屬性

第三回:歷史糾葛:特性和屬性

編輯:.NET實例教程

發布日期:2007.4.19 作者:Anytao

©2007 Anytao.com 轉貼請注明出處,留此信息。

本文將介紹以下內容:

• 定制特性的基本概念和用法

• 屬性與特性的區別比較

• 反射的簡單介紹

1. 引言

attribute是.NET框架引入的有一技術亮點,因此我們有必要花點時間來了解本文的內容,走進一個發現attribute登堂入室的入口。因為.Net Framework中使用了大量的定制特性來完成代碼約定,[Serializable]、[Flags]、[DllImport]、[AttributeUsage]這些的構造,相信我們都見過吧,那麼你是否了解其背後的技術。

提起特性,由於高級語言發展的歷史原因,不免讓人想起另一個耳熟能詳的名字:屬性。特性和屬性,往往給初學者或者從C++轉移到C#的人混淆的概念沖擊。那麼,什麼是屬性,什麼是特性,二者的概念和區別,用法與示例,將在本文做以概括性的總結和比較,希望給你的理解帶來收獲。另外本文的主題以特性的介紹為主,屬性的論述重點突出在二者的比較上,關於屬性的更多論述將在另一篇主題中詳細討論,敬請關注。

2. 概念引入

2.1. 什麼是特性?

MADN的定義為:公共語言運行時允許添加類似關鍵字的描述聲明,叫做attributes, 它對程序中的元素進行標注,如類型、字段、方法和屬性等。Attributes和Microsoft .Net Framework文件的元數據保存在一起,可以用來向運行時描述你的代碼,或者在程序運行的時候影響應用程序的行為。

我們簡單的總結為:定制特性attribute,本質上是一個類,其為目標元素提供關聯附加信息,並在運行期以反射的方式來獲取附加信息。具體的特性實現方法,在接下來的討論中繼續深入。

2.2. 什麼是屬性?

 屬性是面向對象編程的基本概念,提供了對私有字段的訪問封裝,在C#中以get和set訪問器方法實現對可讀可寫屬性的操作,提供了安全和靈活的數據訪問封裝。關於屬性的概念,不是本文的重點,而且相信大部分的技術人員應該對屬性有清晰的概念。以下是簡單的屬性示例:

 



public class MyProperty
    {
        //定義字段
        private string _name;
        private int _age;

        //定義屬性,實現對_name字段的封裝

$False$

/>        public string Name
        {
            get { return (_name == null) ? string.Empty : _name; }
            set { _name = value; }
        }

        //定義屬性,實現對_age字段的封裝
        //加入對字段的范圍控制
        public int Age
        {
            get { return _age; }
            set

00">
            {
                if ((value > 0) && (value < 150))
                {
                    _age = value;
                }
                else
                {
                    throw new Exception("Not a real age");
                }
            }
        }
    }

    public 

="COLOR: #0000ff">class MyTest
    {
        public static void Main(string[] args)
        {
            MyProperty myProperty = new MyProperty();
            //觸發set訪問器
            myProperty.Name = "Anytao";
            //觸發get訪問器
            Console.WriteLine(myProperty.Name);
            myProperty.Age = 66;
            Console.WriteLine(myProperty.Age.ToString());
            Console.ReadLine();
        }


2.3. 區別與比較

通過對概念的澄清和歷史的回溯,我們知道特性和屬性只是在名稱上有過糾葛,在MSDN上關於attribute的中文解釋甚至還是屬性,但是我同意更通常的稱呼:特性。在功能上和應用上,二者其實沒有太多模糊的概念交叉,因此也沒有必要來比較其應用的異同點。本文則以特性的概念為重點,來討論其應用的場合和規則。

我理解的定制特性,就是為目標元素,可以是數據集、模塊、類、屬性、方法、甚至函數參數等加入附加信息,類似於注釋,但是可以在運行期以反射的方式獲得。定制特性主要應用在序列化、編譯器指令、設計模式等方面。

3. 通用規則

  1. 定制特性可以應用的目標元素可以為:程序集(assembly)、模塊(module)、類型(type)、屬性(property)、事件(event)、字段(fIEld)、方法(method)、參數(param)、返回值(return),應該全了。
  2. 定制特性以[,]形式展現,放在緊挨著的元素上,多個特性可以應用於同一元素,特性間以逗號隔開,以下表達規則有效:[AttributeUsage][ Flags]、[AttributeUsage, Flags]、[Flags, AttibuteUsageAttribute]、[AttributeUsage(), FlagesAttribute()]
  3. attibute實例,是在編譯期進行初始化,而不是運行期。
  4. C#允許以指定的前綴來表示特性所應用的目標元素,建議這樣來處理,因為顯式處理可以消除可能帶來的二義性。例如:   


    using System; 

    namespace Anytao.Net 
    {
        [assembly: MyAttribute(1)]          //應用於程序集
        [moduel: MyAttribute(2)]            //應用於模塊
        pubic class Attribute_how2do
        {
            //
        } 
    }
  5. 定制特性類型,必須直接或者間接的繼承自System.Attribute類,而且該類型必須有公有構造函數來創建其實例。
  6. 所有自定義的特性名稱都應該有個Attribute後綴,這是習慣性約定。
  7. 定制特性也可以應用在其他定制特性上,這點也很好理解,因為定制特性本身也是一個類,遵守類的公有規則。例如很多時候我們的自定義定制特性會應用AttributeUsageAttribute特性,來控制如何應用新定義的特性。
  8.    


    [AttributeUsageAttribute(AttributeTarget.All),
    AllowMultiple = true, 
    Inherited = true]
    class MyNewAttribute: System.Attribute
    {
    //

  9. 定制特性不會影響應用元素的任何功能,只是約定了該元素具有的特質。
  10. 所有非抽象特性必須具有public訪問限制。
  11. 特性常用於編譯器指令,突破#define, #undefine, #if, #endif的限制,而且更加靈活。
  12. 定制特性常用於在運行期獲得代碼注釋信息,以附加信息來優化調試。
  13. 定制特性可以應用在某些設計模式中,如工廠模式。
  14. 定制特性還常用於位標記,非托管函數標記、方法廢棄標記等其他方面。

4. 特性的應用

4.1. 常用特性

常用特性,也就是.NET已經提供的固有特性,事實上在.NET框架中已經提供了豐富的固有特性由我們發揮,以下精選出我認為最常用、最典型的固有特性做以簡單討論,當然這只是我的一家之言,亦不足道。我想了解特性,還是從這裡做為起點,從.Net提供的經典開始,或許是一種求知的捷徑,希望能給大家以啟示。

  1. AttributeUsage

    AttributeUsage特性用於控制如何應用自定義特性到目標元素。關於AttributeTargetsAllowMultipleInheritedValidOn,請參閱示例說明和其他文檔。我們已經做了相當的介紹和示例說明,我們還是在實踐中自己體會更多吧。

  2. Flags

    以Flags特性來將枚舉數值看作位標記,而非單獨的數值,例如: 



    enum Animal
    {
        Dog     = 0x0001,
        Cat     = 0x0002,
        Duck    = 

  3. 0x0004,
      Chicken = 0x0008
    }

    因此,以下實現就相當輕松, 



    Animal animals = Animal.Dog | Animal.Cat;
    Console.WriteLine(animals.ToString());

    請猜測結果是什麼,答案是:"Dog, Cat"。如果沒有Flags特別,這裡的結果將是"3"。關於位標記,也將在本系列的後續章回中有所交代,在此只做以探討止步。

DllImport

DllImport特性,可以讓我們調用非托管代碼,所以我們可以使用DllImport特性引入對Win32 API函數的調用,對於習慣了非托管代碼的程序員來說,這一特性無疑是救命的稻草。 

 



using System;
using System.Runtime.InteropServices;

namespace Anytao.Net
{
    class MainClass 
    {
       [DllImport("User32.dll")]
       public static extern int MessageBox(int hParent, string msg, string caption,

 int type);

       static int Main() 
       {
          return MessageBox(0, "How to use attribute in .Net", "Anytao_net", 0);
      }
    }
}

 

  • Serializable

    Serializable特性表明了應用的元素可以被序列化(serializated),序列化和反序列化是另一個可以深入討論的話題,在此我們只是提出概念,深入的研究有待以專門的主題來呈現,限於篇幅,此不贅述。

  • Conditional

    Conditional特性,用於條件編譯,在調試時使用。注意:Conditional不可應用於數據成員和屬性。

  • 還有其他的重要特性,包括:DescriptionDefaultValueCategoryReadOnlyBrowerAble等,有時間可以深入研究。

    4.2. 自定義特性

    既然attribute,本質上就是一個類,那麼我們就可以自定義更特定的attribute來滿足個性化要求,只要遵守上述的12條規則,實現一個自定義特性其實是很容易的,典型的實現方法為:

    1. 定義特性   


      [AttributeUsage(AttributeTargets.Class |
              AttributeTargets.Method,
              Inherited = true)]
    2. OR: #0000ff">public class TestAttribute : System.Attribute
          {
              public TestAttribute(string message)
              {
                  Console.WriteLine(message);
              }
              public void RunTest()
              {
                  Console.WriteLine("TestAttribute here.");
              }
          } 

       

    3. 應用目標元素   


          [Test("Error Here.")]
              public void CannotRun()
              {
                  //
              }

       

    4. 獲取元素附加信息

      如果沒有什麼機制來在運行期來獲取Attribute的附加信息,那麼attribute就沒有什麼存在的意義。

      因此,.Net中以反射機制來實現在運行期獲取attribute信息,實現方法如下:  

       



              public static void Main()
              {
                  Tester t = new Tester();
                  t.CannotRun();

                  Type tp = typeof(Tester);
                  MethodInfo mInfo = tp.GetMethod("CannotRun");            
                  TestAttribute myAtt = (TestAttribute)Attribute.GetCustomAttribute(mInfo, typeof(TestAttribute));
                  myAtt.RunTest();
              }

      5. 經典示例

      5.1 小菜一碟

      啥也不說了,看注釋吧。



      using System;

      using System.Reflection;                                 //應用反射技術獲得特性信息

      namespace Anytao.Net
      {
          //定制特性也可以應用在其他定制特性上,
          //應用AttributeUsage,來控制如何應用新定義的特性
          [AttributeUsageAttribute(AttributeTargets.All,       //可應用任何元素
              AllowMultiple = true,                            //允許應用多次
              Inherited = false)]                              //不繼承到派生類
          //特性也是一個類,


          //必須繼承自System.Attribute類,
          //命名規范為:"類名"+Attribute。        
          public class MyselfAttribute : System.Attribute
          {
              //定義字段
              private string _name;
              private int _age;
              private string _memo;

              //必須定義其構造函數,如果不定義有編譯器提供無參默認構造函數
              public MyselfAttribute()
              {
              }
              public MyselfAttribute(string name,

       int age)
              {
                  _name = name;
                  _age = age;
              }

    5. //定義屬性
              //顯然特性和屬性不是一回事兒
              public string Name
              {
                  get { return _name == null ? string.Empty : _name; }
              }

              public int Age
              {
                  get {

      return _age; }
              }

              public string Memo
              {
                  get { return _memo; }
                  set { _memo = value; }
              }

              //定義方法
              public void ShowName()
              {
                  Console.WriteLine("Hello, {0}", _name == null ? "world." : _name);
              }
          }



          //應用自定義特性
          //可以以Myself或者MyselfAttribute作為特性名
          //可以給屬性Memo賦值
          [Myself("Emma", 25, Memo = "Emma is my good girl.")]
          public class Mytest
          {
              public void SayHello()
              {
                  Console.WriteLine("Hello, my.Net world.");
              }
          }

          public class Myrun
          {
              public 

      OLOR: #0000ff">static void Main(string[] args)
              {
                  //如何以反射確定特性信息
                  Type tp = typeof(Mytest);
                  MemberInfo info = tp;
                  MyselfAttribute myAttribute =
                      (MyselfAttribute)Attribute.GetCustomAttribute(info, typeof(MyselfAttribute));
                  if (myAttribute != null)
                  {
                      //嘿嘿,在運行時查看注釋內容,是不是很爽
                      Console.WriteLine("Name: {0}

      ", myAttribute.Name);
                      Console.WriteLine("Age: {0}", myAttribute.Age);
                      Console.WriteLine("Memo of {0} is {1}", myAttribute.Name, myAttribute.Memo);
                      myAttribute.ShowName();
                  }

                  //多點反射
                  object obj = Activator.CreateInstance(typeof(Mytest));

                  MethodInfo mi = tp.GetMethod("SayHello");
                  mi.Invoke(obj, null);
                  Console.ReadLine();
           &
      nbsp;  }
          }
      }

    6.  啥也別想了,自己做一下試試。

      5.2 他山之石

      • MSDN認為,特性 (Attribute) 描述如何將數據序列化,指定用於強制安全性的特性,並限制實時 (JIT) 編譯器的優化,從而使代碼易於調試。屬性 (Attribute) 還可以記錄文件名或代碼作者,或在窗體開發階段控制控件和成員的可見性。
      • dudu Boss收藏的系列文章《Attribute在.Net編程中的應用》,給你應用方面的啟示會很多,值得研究。
      • 亞歷山大同志 的系列文章《手把手教你寫ORM(六)》中,也有很好的诠釋。
      • idior的文章《Remoting基本原理及其擴展機制》也有收獲,因此補充。

      6. 結論

       Attribute是.NET引入的一大特色技術,但在博客園中討論的不是很多,所以拿出自己的體會來分享,希望就這一技術要點進行一番登堂入室的引導。更深層次的應用,例如序列化、程序安全性、設計模式多方面都可以挖掘出閃耀的金子,這就是.Net在技術領域帶來的百變魅力吧。希望大家暢所欲言,來完善和補充作者在這方面的不全面和認知上的不深入,那將是作者最大的鼓勵和動力。
       

      參考文獻

      (USA)Stanley B.Lippman, C# Primer 

    1. 上一頁:
    2. 下一頁:
    Copyright © 程式師世界 All Rights Reserved