C# 特性

參考:
https://msdn.microsoft.com/zh-cn/library/e8kc3626(v=vs.110)
http://www.runoob.com/csharp/csharp-attribute.html

特性(Attribute)是用於在運行時傳遞程序中各種元素(比如類、方法、結構、枚舉、組件等)的行爲信息的聲明性標籤。您可以通過使用特性向程序添加聲明性信息。一個聲明性標籤是通過放置在它所應用的元素前面的方括號[]來描述的。
特性可以放置在幾乎所有的聲明中。在 C# 中,特性的指定方法爲:將括在方括號中的特性名置於其應用到的實體的聲明上方。如下代碼:

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

一個聲明上可放置多個特性:

using System.Runtime.InteropServices;  

static void MethodA([In][Out] ref double x)  
{  

}  
static void MethodB([Out][In] double x)  
{  

}  
static void MethodC([Out, In]double x)  
{  

}  

某些特性對於給定實體可以指定多次,如, ConditionalAttribute就是一個可以多次使用的特性:

[Conditional("DEBUG"), Conditional("TEST1")]  
void TraceMethod()  
{  
    // ...  
}  

特性參數:
定位參數(特性類的構造函數的參數)、屬性參數(特性類中的屬性),也叫命名參數定位參數是必填的且必須按順序指定, 屬性參數是可選的且可以按任意順序指定。定位參數必須在前。

[DllImport("user32.dll")]  
[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]  
[DllImport("user32.dll", ExactSpelling=false, SetLastError=false)]  

第一個參數(DLL 名稱)是定位參數並且總是第一個出現,其他參數爲命名參數。在這種情況下,兩個命名參數均默認爲 false,因此可將其省略。

接下來看看如何使用自定義特性:

首先我們需要從System.Attribute中派生我們自己的特性類:

public class MyAttribute : Attribute
{

}

接下來就可以用自定義的特性裝飾我們已有的類了:

[My()]
public class MyClass
{

}

這邊提一下,根據約定,所有特性名稱都以單詞“Attribute”結束,以便將它們與“.NET Framework”中的其他項區分。但是,在代碼中使用特性時,不需要指定 attribute 後綴。例如,[DllImport] 雖等效於 [DllImportAttribute],但 DllImportAttribute 纔是該特性在 .NET Framework 中的實際名稱。

現在我們來完善一下這個特性吧,看他的威力到底在哪裏。

[My("this is MyAttribute!!!")]
public class MyClass
{

}

public class MyAttribute : Attribute
{
    public MyAttribute(string str)
    {
        this.str = str;
    }

    private string str;

    public string Str
    {
        get { return str; }
    }
}

測試:

static void Main(string[] args)
{
    MyAttribute myattribute = (MyAttribute)Attribute.GetCustomAttribute(typeof(MyClass), typeof(MyAttribute));

    Console.WriteLine(myattribute.Str);  

    Console.ReadKey();
}

在上面例子中,我們MyAttribute特性添加了一個屬性,並且再Main中查詢它。

接下來看看怎樣定義或控制特性的使用:
AttributeUsage類是預定義特性類之一,它幫助我們控制我們自己的定製特性的使用。它描述了一個定製特性如何被使用。
AttributeUsage有三個屬性,我們可以把它放置在定製屬性前面。第一個屬性是:
* ValidOn
通過這個屬性,我們能夠定義定製特性應該在何種程序實體前放置。一個屬性可以被放置的所有程序實體在AttributeTargets enumerator中列出。通過OR操作我們可以把若干個AttributeTargets值組合起來。
AttributrTargets有以下屬性:
Assembly,Module,Class,Struct,Enum,Constructor,Method,Property,Field,Event,Interface,Parameter,Delegate。

  • AllowMultiple
    這個屬性標記了我們的定製特性能否被重複放置在同一個程序實體前多次。

  • Inherited
    我們可以使用這個屬性來控制定製特性的繼承規則。它標記了我們的特性能否被繼承。

    下面讓我們來做一些實際的東西。我們將會在剛纔的MyAttribute特性前放置AttributeUsage特性以期待在它的幫助下控制MyAttribute特性的使用。

//表示可以定義類的特性,只允許單行,並且不可以被繼承。
[AttributeUsage(AttributeTargets.Class,AllowMultiple = false,Inherited = false)]
public class MyAttribute : Attribute
{
    public MyAttribute(string str)
    {
        this.str = str;
    }

    private string str;

    public string Str
    {
        get { return str; }
    }
}

看看上面例子,表示可以定義類的特性,只允許單行,並且不可以被繼承,接下來來測試下:

public class MyClass
{
    [My("this is MyAttribute!!!")]
    public void Func()
    {
        Console.WriteLine("Func");
    }
}

[AttributeUsage(AttributeTargets.Class,AllowMultiple = false,Inherited = false)]
public class MyAttribute : Attribute
{
    public MyAttribute(string str)
    {
        this.str = str;
    }

    private string str;

    public string Str
    {
        get { return str; }
    }
}

看看上面例子,我們將AttributeTargets設置爲了Class,並且在Func方法上面添加了特性,這是不被允許的,拋出了異常,除非我們將AttributeTargets設置爲Method。接下來我們來看看AllowMultiple:

[My("this is MyAttribute!!!")] 
[My("this is MyAttribute!!!")] 
public class MyClass
{
    public void Func()
    {
        Console.WriteLine("Func");
    }
}

[AttributeUsage(AttributeTargets.Class,AllowMultiple = false,Inherited = false)]
public class MyAttribute : Attribute
{
    public MyAttribute(string str)
    {
        this.str = str;
    }

    private string str;

    public string Str
    {
        get { return str; }
    }
}

看上面這個例子,我們在MyClass上面添加了兩行特性,顯而易見拋出了異常。除非我們將AllowMultiple改爲true。再來看看Inherited:

using System;
using System.Diagnostics;
using System.Reflection;

namespace 特性
{
    class Program
    {
        static void Main()
        {
            MyAttribute myattribute = (MyAttribute)Attribute.GetCustomAttribute(typeof(MyClass), typeof(MyAttribute));

            if (myattribute != null)
                Console.WriteLine(myattribute.Str);
            else 
                Console.WriteLine("特性不存在");

            MyAttribute myattribute1 = (MyAttribute)Attribute.GetCustomAttribute(typeof(NewClass), typeof(MyAttribute));

            if (myattribute1 != null)
                Console.WriteLine(myattribute1.Str);
            else
                Console.WriteLine("特性不存在");

            Console.ReadKey();
        }


        [My("this is MyAttribute!!!")]
        public class MyClass
        {
            public void Func()
            {
                Console.WriteLine("Func");
            }
        }

        public class NewClass : MyClass
        {
            public void NewFunc()
            {
                Console.WriteLine("NewFunc");
            }
        }

        [AttributeUsage(AttributeTargets.Class,AllowMultiple = false,Inherited = false)]
        public class MyAttribute : Attribute
        {
            public MyAttribute(string str)
            {
                this.str = str;
            }

            private string str;

            public string Str
            {
                get { return str; }
            }
        }
    }
}

很顯然輸出:

"this is MyAttribute!!!"
特性不存在

除非我們將Inherited設置爲true。

好了,大概也就這些了,最後我們在看看一個例子,很簡單,只要理解了上面所講的,基本看看代碼就會懂了

using System;
using System.Diagnostics;

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("測試特性的繼承");
        }
    }
    //我們寫一個方法用來獲取特性信息
}

好了,祝進步。

每天進步一點點。

發佈了40 篇原創文章 · 獲贊 7 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章