索引器
索引器(Indexer)是C#引入的一個新型的類成員它使得對象可以像數組那樣被方便直觀的引用索引器非常類似於我們前面講到的屬性但索引器可以有參數列表且只能作用在實例對象上而不能在類上直接作用下面是典型的索引器的設計我們在這裡忽略了具體的實現
class MyClass{ public object this [int index] { get { // 取數據 } set { // 存數據 } }}
索引器沒有像屬性和方法那樣的名字關鍵字this清楚地表達了索引器引用對象的特征和屬性一樣value關鍵字在set後的語句塊裡有參數傳遞意義實際上從編譯後的IL中間語言代碼來看上面這個索引器被實現為
class MyClass{ public object get_Item(int index) { // 取數據 } public void set_Item(int index object value) {//存數據 }}
由於我們的索引器在背後被編譯成get_Item(int index)和set_Item(int index object value)兩個方法我們甚至不能再在聲明實現索引器的類裡面聲明實現這兩個方法編譯器會對這樣的行為報錯這樣隱含實現的方法同樣可以被我們進行調用繼承等操作和我們自己實現的方法別無二致通曉C#語言底層的編譯實現為我們下面理解C#索引器的行為提供了一個很好的基礎
和方法一樣索引器有種存取保護級別和種繼承行為修飾以及外部索引器這些行為同方法沒有任何差別這裡不再贅述唯一不同的是索引器不能為靜態(static)這在對象引用的語義下很容易理解值得注意的是在覆蓋(override)實現索引器時應該用base[E]來存取父類的索引器
和屬性的實現一樣索引器的數據類型同時為get語句塊的返回類型和set語句塊中value關鍵字的類型
索引器的參數列表也是值得注意的地方索引的特征使得索引器必須具備至少一個參數該參數位於this關鍵字之後的中括號內索引器的參數也只能是傳值類型不可以有ref(引用)和out(輸出)修飾參數的數據類型可以是C#中的任何數據類型C#根據不同的參數簽名來進行索引器的多態辨析中括號內的所有參數在get和set下都可以引用而value關鍵字只能在set下作為傳遞參數
下面是一個索引器的具體的應用例子它對我們理解索引器的設計和應用很有幫助
using System;class BitArray{int[] bits;int length;public BitArray(int length) {if (length < ) throw new ArgumentException();bits = new int[((length ) >> ) + ];thislength = length;}public int Length {get { return length; }}public bool this[int index] {get {if (index < || index >= length) throw new IndexOutOfRangeException();elsereturn (bits[index >> ] & << index) != ;}set{if (index < || index >= length)throw new IndexOutOfRangeException();else if(value) bits[index >> ] |= << index;elsebits[index >> ] &= ~( << index);}}}class Test{static void Main() {BitArray Bits=new BitArray();for(int i=;i<;i++)Bits[i]=(i%)==; ConsoleWrite(Bits[i]+ );}}
編譯並運行程序可以得到下面的輸出
True False True False True False True False True False
上面的程序通過索引器的使用為用戶提供了一個界面友好的bool數組同時又大大降低了程序的存儲空間代價索引器通常用於對象容器中為其內的對象提供友好的存取界面這也是為什麼C#將方法包裝成索引器的原因所在實際上我們可以看到索引器在NET Framework類庫中有大量的應用
操作符重載
操作符是C#中用於定義類的實例對象間表達式操作的一種成員和索引器類似操作符仍然是對方法實現的一種邏輯界面抽象也就是說在編譯成的IL中間語言代碼中操作符仍然是以方法的形式調用的在類內定義操作符成員又叫操作符重載C#中的重載操作符共有三種一元操作符二元操作符和轉換操作符並不是所有的操作符都可以重載三種操作符都有相應的可重載操作符集列於下表
一元操作符+ ! ~ ++ true false
二元操作符+ * / % & | ^ << >> == != > < >= <=
轉換操作符隱式轉換()和顯式轉換()
重載操作符必須是public和static 修飾的否則會引起編譯錯誤這在操作符的邏輯語義下是不言而喻的父類的重載操作符會被子類繼承但這種繼承沒有覆蓋隱藏抽象等行為不能對重載操作符進行virtual sealed override abstract修飾操作符的參數必須為傳值參數我們下面來看一個具體的例子
using System;class Complex{double r v; //r+ v ipublic Complex(double r double v){thisr=r;thisv=v;}public static Complex operator +(Complex a Complex b) {return new Complex(ar+br av+bv);}public static Complex operator (Complex a){return new Complex(arav);}public static Complex operator ++(Complex a) { double r=ar+; double v=av+;return new Complex(r v);}public void Print(){ConsoleWrite(r+ + +v+i);}}class Test{public static void Main(){Complex a=new Complex();Complex b=new Complex();Complex c=a;cPrint();Complex d=a+b;dPrint();aPrint();Complex e=a++;aPrint();ePrint();Complex f=++a;aPrint();fPrint();}}
編譯程序並運行可得到下面的輸出
+ i + i + i + i + i + i + i
我們這裡實現了一個+號二元操作符一個號一元操作符(取負值)和一個++一元操作符注意這裡我們都沒有對傳進來的參數作任何改變這在參數是引用類型的變量是尤其重要雖然重載操作符的參數只能是傳值方式而我們在返回值時往往需要new一個新的變量除了true和false操作符這在重載++和 操作符時尤其顯得重要也就是說我們做在a++時我們將丟棄原來的a值而取代的是新的new出來的值給a! 值得注意的是e=a++或f=++a中e的值或f的值根本與我們重載的操作符返回值沒有一點聯系!它們的值僅僅是在前置和後置的情況下獲得a的舊值或新值而已!前置和後置的行為不難理解
操作符重載對返回值和參數類型有著相當嚴格的要求一元操作符中只有一個參數操作符++和返回值類型和參數類型必須和聲明該操作符的類型一樣操作符+ ! ~的參數類型必須和聲明該操作符的類型一樣返回值類型可以任意true和false操作符的參數類型必須和聲明該操作符的類型一樣而返回值類型必須為bool而且必須配對出現也就是說只聲明其中一個是不對的會引起編譯錯誤參數類型的不同會導致同名的操作符的重載實際上這是方法重載的表現
二元操作符參數必須為兩個而且兩個必須至少有一個的參數類型為聲明該操作符的類型返回值類型可以任意有三對操作符也需要必須配對聲明出現它們是==和!=>和<>=和<=需要注意的是兩個參數的類型不同雖然類型相同但順序不同都會導致同名的操作符的重載
轉換操作符為不同類型之間提供隱式轉換和顯式轉換主要用於方法調用轉型表達和賦值操作轉換操作符對其參數類型(被轉換類型)和返回值類型(轉換類型)也有嚴格的要求參數類型和返回值類型不能相同且兩者之間必須至少有一個和定義操作符的類型相同轉換操作符必須定義在被轉換類型或轉換類型任何其中一個裡面不能對系統定義過的轉換操作進行重新定義兩個類型也都不能是object或接口類型兩者之間不能有直接或間接的繼承關系這三種情況系統已經默認轉換我們來看一個例子
using System;public struct Digit{byte value;public Digit(byte value) {if (value < || value > ) throw new ArgumentException();thisvalue = value;}public static implicit operator byte(Digit d) {return dvalue;}public static explicit operator Digit(byte b) {return new Digit(b);}}
上面的例子提供了Digit類型和byte類型之間的隱式轉換和顯式轉換從Digit到byte的轉換為隱式轉換轉換過程不會因為丟失任何信息而拋出異常從byte到Digit的轉換為顯式轉換轉換過程有可能因丟失信息而拋出異常實際上這也為我們揭示了什麼時候聲明隱式轉換什麼時候聲明顯示轉換的設計原則不能對同一參數類型同時聲明隱式轉換和顯式轉換隱式轉換和顯式轉換無需配對使用雖然C#推薦這樣做
實際上可以看到對於屬性索引器和操作符這些C#提供給我們的界面操作都是方法的某種形式的邏輯抽象包裝它旨在為我們定義的類型的用戶提供一個友好易用的界面我們完全可以通過方法來實現它們實現的功能理解了這樣的設計初衷我們才會恰當正確地用好這些操作而不致導致濫用和錯用
From:http://tw.wingwit.com/Article/program/net/201311/13906.html