C#特性(屬性)Attribute

先明確一個概念:html

元數據。.NET中元數據是指程序集中的命名空間、類、方法、屬性等信息。這些信息是能夠經過Reflection讀取出來的。app

再來看個例子:框架

#define BUG
//#define NOBUG
using System; using System.Diagnostics; namespace AttributeExample { public static class AttributeTest { [Conditional("BUG")] public static void OutputDebug(string msg) { Console.WriteLine(msg + " : bug"); } [Conditional("NOBUG")] public static void OutputMessage(string msg) { Console.WriteLine(msg + " : no bug"); } } class Program { static void Main(string[] args) { AttributeTest.OutputDebug("Function"); AttributeTest.OutputMessage("Function"); Console.Read(); } } }

運行結果:ide

將#define BUG註釋掉,#define NOBUG的註釋取消,從新運行的結果以下:函數

 

那麼能夠理解爲,上述代碼中的[Conditional()]起到了條件的做用。這就是一種特性。ui


特性是用於在運行時傳遞程序中各類元素(類、方法、結構、枚舉等)的行爲信息的聲明性標籤。能夠經過使用特性向程序中添加聲明性信息。這些信息添加到了元數據中。.NET框架中的特性包括Common AttributesCustom Attributes。其中Common Attributes包括Global Attributes, Obsolete Attributes, Conditional Attributes, Caller Info Attributes。this

Common Attributesspa

  • Global Attributes 全局特性,應用於整個程序集。置於using後代碼最上端。全局特性提供一個程序集的信息,分爲三種類別:
    • Assembly identity attributes 程序集標識符特性。name, version, and culture這三個特性用於肯定一個程序集的標識符。能夠查看一下項目中Properties下面的AssemblyInfo.cs文件。
    • Informational attributes 信息特性,提供一些與公司或產品相關的信息,包括AssemblyProductAttribute,AssemblyTrademarkAttribute等。
    • Assembly manifest attributes 程序集清單特性。包括title, description, default alias, and configuration。
  • Conditional Attributes 標記了一個條件方法,其執行依賴於指定的預處理標識符(#define),實例就是上面那個。條件特性能夠同時使用多個。好比[Conditional("A"), Conditional("B")]。
  • Obsolete Attributes 標記了不該被使用的程序實體。它可讓您通知編譯器丟棄某個特定的目標元素。其語法表示爲:[Obsolete(message)]、[Obsolete(message, iserror)]

其中,message是一個string,描述項目爲何過期以及該替代使用什麼;iserror是一個bool,若是爲true,編譯器應該把項目的使用當作一個錯誤,默認值是false,此時編譯器生成一個警告。好比下面的例子:.net

using System;
using System.Diagnostics;

namespace AttributeExample
{
    class Program
    {
        [Obsolete("Don't use OldMethod, use NewMethod instead", true)]
        static void OldMethod()
        {
            Console.WriteLine("It is the old method");
        }
        static void NewMethod()
        {
            Console.WriteLine("It is the new method");
        }

        static void Main(string[] args)
        {
            OldMethod();
            Console.Read();
        }
    }
}

這時程序沒法編譯,顯示以下的錯誤:設計

  • Caller Info Attributes 經過使用該特性,能夠獲取調用某個方法的調用者的信息。好比代碼的文件路徑、代碼的行等。好比下面所示的代碼。
using System;
using System.Runtime.CompilerServices;

namespace AttributeExample
{
    class Program
    {
        public static void DoSomething()
        {
            TraceMessage("Something happened.");
        }

        public static void TraceMessage(string message,
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "",
            [CallerLineNumber] int sourceLineNumber = 0)
        {
            Console.WriteLine("message: " + message);
            Console.WriteLine("member name: " + memberName);
            Console.WriteLine("source file path: " + sourceFilePath);
            Console.WriteLine("source line number: " + sourceLineNumber);
        }

        static void Main(string[] args)
        {
            DoSomething();
            Console.Read();
        }
    }
}

運行結果如圖所示:

Custom Attributes

 .NET框架容許建立自定義特性,用於存儲聲明性的信息,且可在運行時被檢索。該信息根據設計標準和應用程序須要,可與任務目標元素相關。建立並使用自定義特性包含四個步驟:

1. 聲明自定義特性

 一個新的自定義特性應派生自System.Attribute類,好比:

   // 聲明名爲FirstAttribute的自定義特性 
  [AttributeUsage(AttributeTargets.Class, Inherited =false)] class FirstAttribute : Attribute { }   
  // 聲明名爲SecondAttribute的自定義特性 [AttributeUsage(AttributeTargets.Class)]
class SecondAttribute : Attribute { }
  // 聲明名爲ThirdAttribute的自定義特性 [AttributeUsage(AttributeTargets.Class, AllowMultiple
=true)] class ThirdAttribute : Attribute { }

又好比:

   // 聲明名爲CustomAttribute的自定義特性 
  [AttributeUsage(AttributeTargets.Class| AttributeTargets.Constructor| AttributeTargets.Field| AttributeTargets.Method| AttributeTargets.Property, AllowMultiple =true)] public class CustomAttributes : System.Attribute{}

2.構建自定義特性

上面已經聲明瞭一個名爲CustomAttribute的自定義特性,如今構建這個特性。該特性將存儲調試程序得到的信息,包括:bug的代碼編號、辨認該bug的開發人員名字、最後一次審查該代碼的日期以及一個存儲了開發人員標記的字符串消息。

CustomAttribute類將帶有三個用於存儲前三個信息的私有屬性和一個用於存儲消息的公有屬性。所以bug編號、開發人員名字和審查日期將是CustomAttribute類的必需的定位(positional)參數,消息將是一個可選的命名(named)參數。每一個特性必須至少有一個構造函數。必需的定位參數應經過構造函數傳遞,以下面的代碼所示:

    [AttributeUsage(AttributeTargets.Class|
        AttributeTargets.Constructor|
        AttributeTargets.Field|
        AttributeTargets.Method|
        AttributeTargets.Property,
        AllowMultiple =true)]
    public class CustomAttributes : System.Attribute
    {
        public int BugNo { get; }
        public string Developer { get; }
        public string LastReview { get; }
        public string Message { get; set; }

        public CustomAttributes(int BugNo, string Developer, string LastReview)
        {
            this.BugNo = BugNo;
            this.Developer = Developer;
            this.LastReview = LastReview;
        }
    }

3.在目標程序元素上應用自定義特性

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

    [CustomAttributes(45,"Zara Ali","12/8/2012", Message ="Return type mismatch")]
    [CustomAttributes(49,"Nuha Ali","10/10/2012",Message ="Unused variable")]
    class Rectangle
    {
        protected double length;
        protected double width;
        public Rectangle(double length, double width)
        {
            this.length = length;
            this.width = width;
        }

        [CustomAttributes(55,"Zara Ali","19/10/2012", Message ="Return type mismatch")]
        public double GetArea()
        {
            return length * width;
        }

        [CustomAttributes(56,"Zara Ali", "19/10/2012")]
        public void Display()
        {
            Console.WriteLine("Length: {0}", length);
            Console.WriteLine("Width: {0}", width);
            Console.WriteLine("Area: {0}", GetArea());
        }
    }    

4.經過反射訪問特性

別忘了使用using System.Reflection;

class ExecuteRectangle
    {
        static void Main(string[] args)
        {
            Rectangle r = new Rectangle(4.5, 7.5);
            r.Display();
            object[] attrs = r.GetType().GetCustomAttributes(false);
            // 遍歷Rectangle類的特性
            foreach(Attribute attr in attrs)
            {
                CustomAttributes attribute = (CustomAttributes)attr;
                if(null != attribute)
                {
                    Console.WriteLine("Bug no: {0}", attribute.BugNo);
                    Console.WriteLine("Developor: {0}", attribute.Developer);
                    Console.WriteLine("Last Reviewed: {0}", attribute.LastReview);
                    Console.WriteLine("Remarks: {0}", attribute.Message);
                }
            }

            // 遍歷方法特性
            object[] methods = r.GetType().GetMethods();
            foreach(MethodInfo method in methods)
            {
                foreach(object attribute in method.GetCustomAttributes(true))
                {
                    CustomAttributes attr = attribute as CustomAttributes;
                    if(null != attr)
                    {
                        Console.WriteLine("Bug no: {0}, for Method: {1}", attr.BugNo, method.Name);
                        Console.WriteLine("Developer: {0}", attr.Developer);
                        Console.WriteLine("Last Reviewed: {0}", attr.LastReview);
                        Console.WriteLine("Remarks: {0}", attr.Message);
                    }
                }
            }
            Console.Read();
        }
    }

運行結果如圖所示:

 

參考文獻:

1.https://www.runoob.com/csharp/csharp-attribute.html

2.https://www.runoob.com/csharp/csharp-reflection.html

3.https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/

4.https://blog.csdn.net/xiaouncle/article/details/70216951

相關文章
相關標籤/搜索