24. 特性(Attribute)
概述:特性是指可以對類、以及程序集中的成員進(jìn)行進(jìn)一步的描述,比如寫一個關(guān)于人的類Person,該類可以對人的屬性以及某些行為(方法)進(jìn)行描述。如果要對人類進(jìn)行進(jìn)一步描述,比如人這個類是屬于動物的靈長類動物。有人會說可以為這個Person類去寫一個靈長動物類的父類,再用人類去繼承這個類去解決。但要求僅僅是描述性的,就是對這個人類進(jìn)行進(jìn)一步的描述,而在實(shí)際操作中不需要去操作。這種情況就可以用特性的概念去解決,特性簡單的理解就是程序集的特定程序元素具有另外的性質(zhì)。
- 特性(Attribute)是用于在運(yùn)行時傳遞程序中各種元素(比如類、方法、結(jié)構(gòu)、枚舉、組件等)的行為信息的聲明性標(biāo)簽。您可以通過使用特性向程序添加聲明性信息。一個聲明性標(biāo)簽是通過放置在它所應(yīng)用的元素前面的方括號([ ])來描述的。
- 特性(Attribute)用于添加元數(shù)據(jù),如編譯器指令和注釋、描述、方法、類等其他信息。.Net 框架提供了兩種類型的特性:預(yù)定義特性和自定義特性。
規(guī)定特性(Attribute)
規(guī)定特性(Attribute)的語法如下:
[attribute(positional_parameters, name_parameter = value, ...)]
element
- 特性(Attribute)的名稱和值是在方括號內(nèi)規(guī)定的,放置在它所應(yīng)用的元素之前。positional_parameters 規(guī)定必需的信息,name_parameter 規(guī)定可選的信息。
24.1 預(yù)定義特性(Attribute)
24.1.1 AttributeUsage
預(yù)定義特性 AttributeUsage 描述了如何使用一個自定義特性類。它規(guī)定了特性可應(yīng)用到的項(xiàng)目的類型。
[AttributeUsage(
validon,
AllowMultiple=allowmultiple,
Inherited=inherited
)]
- 參數(shù) validon 規(guī)定特性可被放置的語言元素。它是枚舉器 AttributeTargets 的值的組合。默認(rèn)值是 AttributeTargets.All。
- 參數(shù) allowmultiple(可選的)為該特性的 AllowMultiple 屬性(property)提供一個布爾值。如果為 true,則該特性是多用的。默認(rèn)值是 false(單用的)。
- 參數(shù) inherited(可選的)為該特性的 Inherited 屬性(property)提供一個布爾值。如果為 true,則該特性可被派生類繼承。默認(rèn)值是 false(不被繼承)。
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
24.1.2 Conditional
這個預(yù)定義特性標(biāo)記了一個條件方法,其執(zhí)行依賴于指定的預(yù)處理標(biāo)識符。
它會引起方法調(diào)用的條件編譯,取決于指定的值,比如 Debug 或 Trace。例如,當(dāng)調(diào)試代碼時顯示變量的值。
[Conditional(
conditionalSymbol
)]
[Conditional("DEBUG")]
24.1.3 Obsolete
這個預(yù)定義特性標(biāo)記了不應(yīng)被使用的程序?qū)嶓w。它可以讓您通知編譯器丟棄某個特定的目標(biāo)元素。例如,當(dāng)一個新方法被用在一個類中,但是您仍然想要保持類中的舊方法,您可以通過顯示一個應(yīng)該使用新方法,而不是舊方法的消息,來把它標(biāo)記為 obsolete(過時的)。
[Obsolete(
message
)]
[Obsolete(
message,
iserror
)]
參數(shù) message,是一個字符串,描述項(xiàng)目為什么過時以及該替代使用什么。
參數(shù) iserror,是一個布爾值。如果該值為 true,編譯器應(yīng)把該項(xiàng)目的使用當(dāng)作一個錯誤。默認(rèn)值是 false(編譯器生成一個警告)。
24.2 創(chuàng)建自定義特性(Attribute)
.Net 框架允許創(chuàng)建自定義特性,用于存儲聲明性的信息,且可在運(yùn)行時被檢索。該信息根據(jù)設(shè)計標(biāo)準(zhǔn)和應(yīng)用程序需要,可與任何目標(biāo)元素相關(guān)。創(chuàng)建并使用自定義特性包含四個步驟:
- 聲明自定義特性
- 構(gòu)建自定義特性
- 在目標(biāo)程序元素上應(yīng)用自定義特性
- 通過反射訪問特性
25. 反射(Reflection)
- 反射指程序可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力。
- 程序集包含模塊,而模塊包含類型,類型又包含成員。反射則提供了封裝程序集、模塊和類型的對象。
- 您可以使用反射動態(tài)地創(chuàng)建類型的實(shí)例,將類型綁定到現(xiàn)有對象,或從現(xiàn)有對象中獲取類型。然后,可以調(diào)用類型的方法或訪問其字段和屬性。
優(yōu)點(diǎn):
1、反射提高了程序的靈活性和擴(kuò)展性。
2、降低耦合性,提高自適應(yīng)能力。
3、它允許程序創(chuàng)建和控制任何類的對象,無需提前硬編碼目標(biāo)類。
缺點(diǎn):
1、性能問題:使用反射基本上是一種解釋操作,用于字段和方法接入時要遠(yuǎn)慢于直接代碼。因此反射機(jī)制主要應(yīng)用在對靈活性和拓展性要求很高的系統(tǒng)框架上,普通程序不建議使用。
2、使用反射會模糊程序內(nèi)部邏輯;程序員希望在源代碼中看到程序的邏輯,反射卻繞過了源代碼的技術(shù),因而會帶來維護(hù)的問題,反射代碼比相應(yīng)的直接代碼更復(fù)雜。
反射(Reflection)的用途
- 允許在運(yùn)行時查看特性(attribute)信息。
- 允許審查集合中的各種類型,以及實(shí)例化這些類型。
- 允許延遲綁定的方法和屬性(property)。
- 允許在運(yùn)行時創(chuàng)建新類型,然后使用這些類型執(zhí)行一些任務(wù)。
using System;
// 對人類進(jìn)行動物類描述。即在人類的定義前面加:
[Animal(IsPrimate = true)]//為人類加特性,指定人類是靈長類。
class Person
{
//人的姓名儲存字段和屬性
private string name;
public string Name
{
set { name = value; }
get { return name; }
}
//人的年齡儲存字段和屬性
private int age;
public int Age
{
set { age = value; }
get { return age; }
}
//人的性別儲存字段和屬性
private char sex;
public char Sex
{
set { sex = value; }
get { return sex; }
}
//人的打招呼方法
public void SayHello()
{
Console.WriteLine("大家好,我叫{0},我今年{1}歲了,我的性別是{2}", this.Name, this.Age, this.Sex);
}
}
// 動物的特性類AnimalAttribute類繼承于Attribute(特性)
class AnimalAttribute : Attribute
{
//字段和屬性描述是否是靈長類
private bool isPrimate;
public bool IsPrimate
{
set { isPrimate = value; }
get { return isPrimate; }
}
}
class Test
{
static void Main(string[] args)
{
// 通過代碼來獲得人類的特性:
//聲明特性對象,并通過Attribute類的靜態(tài)方法GetCustomAttribute()獲得人類的在動物類的特性,并賦值給特性對象
Attribute att1 = Attribute.GetCustomAttribute(typeof(Person), typeof(AnimalAttribute));
//將特性對象轉(zhuǎn)化為動物特性對象
AnimalAttribute animalAtt = att1 as AnimalAttribute;
//檢查轉(zhuǎn)化是否成功如果成功則打印這個特性對象的是否是靈長類的屬性。
if (animalAtt != null)
{
Console.WriteLine("人類是否是靈長類:{0}", animalAtt.IsPrimate);
}
Console.ReadKey();
}
}
26. 屬性(Property)
域Field 與 屬性Property{get,set}
- 屬性 是類(class)、結(jié)構(gòu)(structure)和接口(interface)的命名(named)成員。
- 類或結(jié)構(gòu)中的成員變量或方法稱為 域(Field)。
- 屬性是域(Field)的擴(kuò)展,且可使用相同的語法來訪問。
- 屬性使用 訪問器(accessors) 讓私有域的值可被讀寫或操作。
- 屬性不會確定存儲位置。相反,它們具有可讀寫或計算它們值的 訪問器(accessors)。
- 訪問器(Accessors):屬性的訪問器(accessor)包含有助于獲?。ㄗx取或計算)或設(shè)置(寫入)屬性的可執(zhí)行語句。訪問器(accessor)聲明可包含一個 get 訪問器、一個 set 訪問器,或者同時包含二者。
- 抽象屬性(Abstract Properties):抽象類可擁有抽象屬性,這些屬性應(yīng)在派生類中被實(shí)現(xiàn)。
例如,有一個名為 Student 的類,帶有 age、name 和 code 的私有域。我們不能在類的范圍以外直接訪問這些域,但是我們可以擁有訪問這些私有域的屬性。
27. 索引器(Indexer)
- 索引器(Indexer) 允許一個對象可以像數(shù)組一樣使用下標(biāo)的方式來訪問.
- 當(dāng)您為類定義一個索引器時,該類的行為就會像一個 虛擬數(shù)組(virtual array) 一樣。您可以使用數(shù)組訪問運(yùn)算符 [ ] 來訪問該類的的成員。
- 索引器的行為的聲明在某種程度上類似于屬性(property)。就像屬性(property),您可使用 get 和 set 訪問器來定義索引器。但是,屬性返回或設(shè)置一個特定的數(shù)據(jù)成員,而索引器返回或設(shè)置對象實(shí)例的一個特定值。換句話說,它把實(shí)例數(shù)據(jù)分為更小的部分,并索引每個部分,獲取或設(shè)置每個部分。
- 定義一個屬性(property)包括提供屬性名稱。索引器定義的時候不帶有名稱,但帶有 this 關(guān)鍵字,它指向?qū)ο髮?shí)例。
- 索引器(Indexer)可被重載。索引器聲明的時候也可帶有多個參數(shù),且每個參數(shù)可以是不同的類型。沒有必要讓索引器必須是整型的。C# 允許索引器可以是其他類型.
element-type this[int index]
{
// get 訪問器
get
{
// 返回 index 指定的值
}
// set 訪問器
set
{
// 設(shè)置 index 指定的值
}
}
using System;
namespace IndexerApplication
{
class IndexedNames
{
private string[] namelist = new string[size];
static public int size = 10;
public IndexedNames()
{
for (int i = 0; i < size; i++)
{
namelist[i] = "N. A.";
}
}
public string this[int index]
{
get
{
string tmp;
if( index >= 0 && index <= size-1 )
{
tmp = namelist[index];
}
else
{
tmp = "";
}
return ( tmp );
}
set
{
if( index >= 0 && index <= size-1 )
{
namelist[index] = value;
}
}
}
public int this[string name]
{
get
{
int index = 0;
while(index < size)
{
if (namelist[index] == name)
{
return index;
}
index++;
}
return index;
}
}
static void Main(string[] args)
{
IndexedNames names = new IndexedNames();
names[0] = "Zara";
names[1] = "Riz";
names[2] = "Nuha";
names[3] = "Asif";
names[4] = "Davinder";
names[5] = "Sunil";
names[6] = "Rubic";
// 使用帶有 int 參數(shù)的第一個索引器
for (int i = 0; i < IndexedNames.size; i++)
{
Console.WriteLine(names[i]);
}
// 使用帶有 string 參數(shù)的第二個索引器
Console.WriteLine(names["Nuha"]);
Console.ReadKey();
}
}
}