陳翠娥 王學伶
(1.長沙民政職業技術學院軟件學院,湖南 長沙 410004;2.國網河北保定供電公司,河北 保定 071051)
C#是一種編程語言,它是為生成在.NET Framework上運行的各種應用程序而設計的。C#語言簡單、功能強大、類型安全,而且是面向對象的。C#憑借在許多方面的創新,在保持C語言風格的表現力和雅致特征的同時,實現了應用程序的快速開發。C#的屬性(Properties)、特性(Attributes)、反射(Reflection)等功能在學習的時候較難理解。本文探索這幾個功能及應用,以幫助讀者深入理解C#的這幾個獨特功能。
屬性是一種成員,它提供靈活的機制來讀取、寫入或計算私有字段的值。屬性可用作公共數據成員,但它們實際上是稱為“訪問器”的特殊方法。這樣可以輕松訪問數據,還有助于提高方法的安全性和靈活性。
如下所示的代碼段展示了屬性的應用:

屬性使用時要注意幾個地方:
(1)屬性是允許類公開獲取和設置值的公共方法,而隱藏了實現或驗證代碼。
(2)get屬性訪問器用于返回屬性值,而set訪問器用于分配新值,這些訪問器可以具有不同的訪問級別,如public int Hour{get{return hour;}protected set{hour=value;}}
(3)value關鍵字用于定義由set訪問器分配的值。
(4)不實現set訪問器的屬性均為只讀。
(5)對于不需要任何自定義訪問器代碼的簡單屬性,請考慮選擇使用自動實現的屬性的選項,如public int Hour{get;set;}
元數據是有關在程序中定義的類型的信息。所有的.NET程序集都包含指定的一組元數據,這些元數據描述在程序集中定義的類型和類型成員。
元數據用以對存儲在公共語言運行時可移植可執行文件(PE)文件或存儲在內存中的程序進行描述。將C#代碼編譯為PE文件時,便會將元數據插入到該文件的一部分中,而將代碼轉換為Microsoft中間語言(MSIL)并將其插入到該文件的另一部分中。在模塊或程序集中定義和引用的每個類型和成員都將在元數據中進行說明。當執行代碼時,運行時將元數據加載到內存中,并引用它來發現有關代碼的類、成員、繼承等信息。元數據以非特定語言的方式描述在代碼中定義的每一類型和成員。通過MSIL反匯編工具打開可執行文件,可以查看到完整的元數據信息。元數據存儲以下信息:
(1)程序集的說明
①標識(名稱、版本、區域性、公鑰)
②導出的類型
③該程序集所依賴的其他程序集
④運行所需的安全權限
(2)類型的說明
①名稱、可見性、基類和實現的接口
②成員(方法、字段、屬性、事件、嵌套的類型)
(3)屬性
修飾類型和成員的其他說明性元素
特性和屬性是完全不同的兩種機制,屬性用作類的成員,而特性是為應用程序提供元數據信息的一種機制。特性提供功能強大的方法,用以將元數據或聲明信息與代碼(程序集、類型、方法、屬性等)相關聯。特性與程序實體關聯后,即可在運行時使用名為“反射”的技術查詢特性。特性有如下特點:
(1)特性可向程序中添加元數據;
(2)可以添加自定義特性,以指定所需的任何附加信息;
(3)可以將一個或多個特性應用到整個程序集、模塊或較小的程序元素(如類和方法);
(4)特性可以與方法和屬性相同的方式接受參數;
(5)程序可以使用反射檢查自己的元數據或其它程序內的元數據。
特性可以放置在幾乎所有的聲明中。在C#中,特性的指定方法為:將括在方括號中的特性名置于其應用到的實體的聲明上方。可通過下列過程將特性應用到代碼元素:
(1)定義新特性,或者通過從.NET Framework導入特性的命名空間,使用預定義特性,如Conditional、WebMethod、DllImport、Obsolete等特性;
(2)在緊鄰代碼元素之前放置特性,從而將該特性應用于代碼元素;
(3)為特性指定位置參數和命名參數。

如上所示的代碼段示例了預定義屬性Obsolete的使用方法,上例中第2個參數為false,在Main方法中調用A()將出現警告,如果為true,則會出現語法錯。
下文將通過“編寫代碼實現添加漏洞修復報告”示例來說明自定義特性的應用。

如上所示的代碼段聲明了一個自定義特性類BugFixingAttribute,它必須繼承Attribute類,此例中包括定位參數(必須)和命名參數(可選)。

如上所示的代碼段在類和方法上應用了自定義特性。編寫以下測試代碼進行加法運算,程序運行后,特性即保存至可執行文件中,可以通過反射查詢。

.Net的應用程序由以下幾個部分組成:程序集(Assembly)、模塊(Module)、類型(class)。反射提供一種編程的方式,讓程序員可以在程序運行時獲得這幾個組成部分的相關信息。例如,通過反射可以在運行時獲得.NET中的每一個類型(包括類、結構、委托、接口和枚舉等)的成員,包括方法、屬性、事件,以及構造函數等,還可以獲得每個成員的名稱、限定符和參數等等。可以使用反射動態創建類型的實例,將類型綁定到現有對象,或從現有對象獲取類型并調用其方法或訪問其字段和屬性。例如,如果獲得了構造函數的信息,即可直接創建對象,即使這個對象的類型在編譯時還不知道。
實現反射需要使用到如下類:Assembly、Type、MethodInfo、FieldInfo、EventInfo等,這些類都包含在System.Reflection命名空間下。其中Assembly類可以定義和加載程序集,加載在程序集清單中列出模塊,以及從此程序集中查找類型并創建該類型的實例。Type類可以獲得對象的類型信息,此信息包含對象的所有要素:方法、構造器、屬性等等,通過Type類可以得到這些要素信息,并且調用之。MethodInfo包含方法的信息,通過這個類可以得到方法的名稱、參數、返回值等,并且可以調用之。
反射在下列情況下很有用:
(1)當需要訪問程序元數據中的特性時;
(2)檢查和實例化程序集中的類型;
(3)在運行時構建新類型;
(4)執行后期綁定,訪問在運行時創建的類型的方法。
下面的代碼段通過反射查看前面例題中的特性,代碼中使用到了Type類及GetCustomAttributes()方法、GetMethods()方法,MethodInfo類。

反射提供了以上文字描述中的功能,但是反射也不是萬能的。使用反射時要注意以下幾點:
(1)現實應用程序中很少需要使用反射類型;
(2)使用反射動態綁定需要犧牲性能;
(3)有些元數據信息是不能通過反射獲取的;
(4)某些反射類型是專門為那些CLR開發編譯器開發的,所以你要意識到不是所有的反射類型都是適合所有需求的。
本文作者在多年教學過程中發現初學者對C#中屬性、特性和反射的理解比較困難,因此撰寫本文從實際應用的角度出發通過具體示例簡要地介紹了它們的原理和機制。
[1]史浩.VS C#泛型、多態及反射[J].福建電腦,2014,(11).
[2]王毅.淺析C#反射機制應用及效率[J].大科技,2013,(9).
[3]郭慶華,朱戰立.利用C#.Net反射技術實現軟件界面動態存儲[J].電腦知識與技術,2010,(1).