C#學習筆記 -- Attribute


學習參考:

今天在討論IPC通信契約的時候,盧工提到使用Attribute來描述具體的接口方法的命令信息。發現對 Attribute的概念還不是很熟悉,因此對其進行學習梳理。

1、Attribute是什么?它有什么用?

先來看看官方的定義:

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

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

看定義總是有距離感,還是看看實際的應用吧。

2、常用場景

.NET中常見的Attribute:

  • Conditional:起條件編譯的作用,只有滿足條件,才允許編譯器對它的代碼進行編譯。一般在程序調試的時候使用。
  • DllImport:用來標記非.NET的函數,表明該方法在一個外部的DLL中定義。
  • Obsolete:這個屬性用來標記當前的方法已經被廢棄,不再使用了。
  • Serializable:表明應用的元素可以被序列化
#define DEBUG //這里定義條件

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace AttributeDemo
{
    class MainProgramClass
    {

        [DllImport("User32.dll")]
        public static extern int MessageBox(int hParent, string Message, string Caption, int Type);

        static void Main(string[] args)
        {
            DisplayRunningMessage();
            DisplayDebugMessage();

            MessageBox(0, "Hello", "Message", 0);

            Console.ReadLine();
        }

        [Conditional("DEBUG")]
        private static void DisplayRunningMessage()
        {
            Console.WriteLine("開始運行Main子程序。當前時間是" + DateTime.Now);
        }

        [Conditional("DEBUG")]
        [Obsolete]
        private static void DisplayDebugMessage()
        {
            Console.WriteLine("開始Main子程序");
        }
    }
}

如果在一個程序元素前面聲明一個Attribute,那么就表示這個Attribute被施加到該元素上,前面的代碼,[DllImport]施加到MessageBox函數上, [Conditional]施加到DisplayRuntimeMessage方法和DisplayDebugMessage方法,[Obsolete]施加到DisplayDebugMessage方法上。Attribute類是在編譯的時候被實例化的,而不是像通常的類那樣在運行時候才實例化。

3、自定義特性

1、自定義的Attribute必須直接或者間接繼承System.Attribute。

2、所有自定義的特性名稱都應該有個Attribute后綴,命名規范為:"類名"+Attribute
3、使用AttributeUsage來限定你的Attribute 所施加的元素的類型,AttributeUsage本身也是一個Attribute。它有一個帶參數的構造器,這個參數是AttributeTargets的枚舉類型

public enum AttributeTargets
{
   All=16383,
   Assembly=1,
   Module=2,
   Class=4,
   Struct=8,
   Enum=16,
   Constructor=32,
   Method=64,
   Property=128,
   Field=256,
   Event=512,
   Interface=1024,
   Parameter=2048,
   Delegate=4096,
   ReturnValue=8192
}

此外,AttributeUsage還定義了以下三個屬性:

AllowMultiple::讀取或者設置這個屬性,表示是否可以對一個程序元素施加多個Attribute 。

Inherited:讀取或者設置這個屬性,表示是否施加的Attribute 可以被派生類繼承或者重載。

ValidOn::讀取或者設置這個屬性,指明Attribute 可以被施加的元素的類型。

下面是一個自定義特性的例子,摘自這里

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;
        }

        //定義屬性
        //顯然特性和屬性不是一回事兒
        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 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();
        }
    }
}

 

4、使用FlagsAttribute修飾枚舉

FlagsAttribute屬性就是枚舉類型的一項可選屬性。它的主要作用是可以將枚舉作為位域處理,所謂位域是單個存儲單元內相鄰二進制位的集合。在.Net framework中有很多枚舉都是用FlagsAttribute特性修飾,例如:正則表達式選項System.Text.RegularExpressions.RegexOptions、文件監視中的文件改變類型System.IO.WatcherChangeTypes、System.Web.UI.WebControls.DataControlRowState等等。

使用FlagsAttribute需要注意:

1、只有要對數值執行按位運算(AND、OR、XOR)時才對枚舉使用 FlagsAttribute 自定義屬性。

2.、必須用 2 的冪(即 1、2、4、8 等)定義枚舉常量。

using System;

class FlagsAttributeDemo
{
    enum Color1 : short
    {
        Black = 0,
        Red = 1,
        Green = 2,
        Blue = 4
    };

    [FlagsAttribute]
    enum Color2 : short
    {
        Black = 0,
        Red = 1,
        Green = 2,
        Blue = 4
    };

    static void Main()
    {
        Console.WriteLine("測試未使用FlagsAttribute屬性");
        Color1 MyColor1 = Color1.Red | Color1.Blue & Color1.Green;
        //我先不運行計算一下看看是那個:0001|0100&0010=0001  應該是Red
        Console.WriteLine("MyColor1={0}", MyColor1);
        Color1 MyColor_1 = Color1.Red | Color1.Blue;
        //我先不運行計算一下看看是那個:0001|0100=0101  應該是5
        Console.WriteLine("MyColor_1={0}",MyColor_1);
        Console.WriteLine("測試使用FlagsAttribute屬性");
        Color2 MyColor2 = Color2.Red | Color2.Blue;
        //我先不運行計算一下看看是那個:0001|0100=0101應該是Red,Blue
        Console.WriteLine("MyColor2={0}", MyColor2);
        Console.ReadKey();
    }
}


免責聲明!

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



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