C#锐利体验-第八讲 索引器与操作符重载(转)
第八講 索引器與操作符重載
南京郵電學(xué)院 李建忠(cornyfield@263.net)
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
索引器
索引器(Indexer)是C#引入的一個(gè)新型的類成員,它使得對(duì)象可以像數(shù)組那樣被方便,直觀的引用。索引器非常類似于我們前面講到的屬性,但索引器可以有參數(shù)列表,且只能作用在實(shí)例對(duì)象上,而不能在類上直接作用。下面是典型的索引器的設(shè)計(jì),我們?cè)谶@里忽略了具體的實(shí)現(xiàn)。
class MyClass { public object this [int index] { get { // 取數(shù)據(jù) } set { // 存數(shù)據(jù) } } }索引器沒(méi)有像屬性和方法那樣的名字,關(guān)鍵字this清楚地表達(dá)了索引器引用對(duì)象的特征。和屬性一樣,value關(guān)鍵字在set后的語(yǔ)句塊里有參數(shù)傳遞意義。實(shí)際上從編譯后的IL中間語(yǔ)言代碼來(lái)看,上面這個(gè)索引器被實(shí)現(xiàn)為:
class MyClass { public object get_Item(int index) { // 取數(shù)據(jù) } public void set_Item(int index, object value) { //存數(shù)據(jù) } }由于我們的索引器在背后被編譯成get_Item(int index)和set_Item(int index, object value)兩個(gè)方法,我們甚至不能再在聲明實(shí)現(xiàn)索引器的類里面聲明實(shí)現(xiàn)這兩個(gè)方法,編譯器會(huì)對(duì)這樣的行為報(bào)錯(cuò)。這樣隱含實(shí)現(xiàn)的方法同樣可以被我們進(jìn)行調(diào)用,繼承等操作,和我們自己實(shí)現(xiàn)的方法別無(wú)二致。通曉C#語(yǔ)言底層的編譯實(shí)現(xiàn)為我們下面理解C#索引器的行為提供了一個(gè)很好的基礎(chǔ)。
和方法一樣,索引器有5種存取保護(hù)級(jí)別,和4種繼承行為修飾,以及外部索引器。這些行為同方法沒(méi)有任何差別,這里不再贅述。唯一不同的是索引器不能為靜態(tài)(static),這在對(duì)象引用的語(yǔ)義下很容易理解。值得注意的是在覆蓋(override)實(shí)現(xiàn)索引器時(shí),應(yīng)該用base[E]來(lái)存取父類的索引器。
和屬性的實(shí)現(xiàn)一樣,索引器的數(shù)據(jù)類型同時(shí)為get語(yǔ)句塊的返回類型和set語(yǔ)句塊中value關(guān)鍵字的類型。
索引器的參數(shù)列表也是值得注意的地方。“索引”的特征使得索引器必須具備至少一個(gè)參數(shù),該參數(shù)位于this關(guān)鍵字之后的中括號(hào)內(nèi)。索引器的參數(shù)也只能是傳值類型,不可以有ref(引用)和out(輸出)修飾。參數(shù)的數(shù)據(jù)類型可以是C#中的任何數(shù)據(jù)類型。C#根據(jù)不同的參數(shù)簽名來(lái)進(jìn)行索引器的多態(tài)辨析。中括號(hào)內(nèi)的所有參數(shù)在get和set下都可以引用,而value關(guān)鍵字只能在set下作為傳遞參數(shù)。
下面是一個(gè)索引器的具體的應(yīng)用例子,它對(duì)我們理解索引器的設(shè)計(jì)和應(yīng)用很有幫助。
using System; class BitArray { int[] bits; int length; public BitArray(int length) { if (length < 0) throw new ArgumentException(); bits = new int[((length - 1) >> 5) + 1]; this.length = length; } public int Length { get { return length; } } public bool this[int index] { get { if (index < 0 || index >= length) throw new IndexOutOfRangeException(); else return (bits[index >> 5] & 1 << index) != 0; } set { if (index < 0 || index >= length) throw new IndexOutOfRangeException(); else if(value) bits[index >> 5] |= 1 << index; else bits[index >> 5] &= ~(1 << index); } } } class Test { static void Main() { BitArray Bits=new BitArray(10); for(int i=0;i<10;i++) Bits[i]=(i%2)==0; Console.Write(Bits[i]+" "); } }編譯并運(yùn)行程序可以得到下面的輸出:
- True False True False True False True False True False
上面的程序通過(guò)索引器的使用為用戶提供了一個(gè)界面友好的bool數(shù)組,同時(shí)又大大降低了程序的存儲(chǔ)空間代價(jià)。索引器通常用于對(duì)象容器中為其內(nèi)的對(duì)象提供友好的存取界面--這也是為什么C#將方法包裝成索引器的原因所在。實(shí)際上,我們可以看到索引器在.NET Framework類庫(kù)中有大量的應(yīng)用。
操作符重載
操作符是C#中用于定義類的實(shí)例對(duì)象間表達(dá)式操作的一種成員。和索引器類似,操作符仍然是對(duì)方法實(shí)現(xiàn)的一種邏輯界面抽象,也就是說(shuō)在編譯成的IL中間語(yǔ)言代碼中,操作符仍然是以方法的形式調(diào)用的。在類內(nèi)定義操作符成員又叫操作符重載。C#中的重載操作符共有三種:一元操作符,二元操作符和轉(zhuǎn)換操作符。并不是所有的操作符都可以重載,三種操作符都有相應(yīng)的可重載操作符集,列于下表:
- 一元操作符 + - ! ~ ++ -- true false
二元操作符 + - * / % & | ^ << >> == != > < >= <=
轉(zhuǎn)換操作符 隱式轉(zhuǎn)換()和顯式轉(zhuǎn)換()
重載操作符必須是public和static 修飾的,否則會(huì)引起編譯錯(cuò)誤,這在操作符的邏輯語(yǔ)義下是不言而喻的。父類的重載操作符會(huì)被子類繼承,但這種繼承沒(méi)有覆蓋,隱藏,抽象等行為,不能對(duì)重載操作符進(jìn)行virtual sealed override abstract修飾。操作符的參數(shù)必須為傳值參數(shù)。我們下面來(lái)看一個(gè)具體的例子:
using System;class Complex{double r, v; //r+ v ipublic Complex(double r, double v){this.r=r;this.v=v;}public static Complex operator +(Complex a, Complex b){return new Complex(a.r+b.r, a.v+b.v);}public static Complex operator -(Complex a){return new Complex(-a.r,-a.v);}public static Complex operator ++(Complex a){double r=a.r+1;double v=a.v+1;return new Complex(r, v);}public void Print(){Console.Write(r+" + "+v+"i");}}class Test{public static void Main(){Complex a=new Complex(3,4);Complex b=new Complex(5,6);Complex c=-a;c.Print();Complex d=a+b;d.Print();a.Print();Complex e=a++;a.Print();e.Print();Complex f=++a;a.Print();f.Print();}}編譯程序并運(yùn)行可得到下面的輸出:
- -3 + -4i 8 + 10i 3 + 4i 4 + 5i 3 + 4i 5 + 6i 5 + 6i
我們這里實(shí)現(xiàn)了一個(gè)“+”號(hào)二元操作符,一個(gè)“-”號(hào)一元操作符(取負(fù)值),和一個(gè)“++”一元操作符。注意這里,我們都沒(méi)有對(duì)傳進(jìn)來(lái)的參數(shù)作任何改變--這在參數(shù)是引用類型的變量是尤其重要,雖然重載操作符的參數(shù)只能是傳值方式。而我們?cè)诜祷刂禃r(shí),往往需要“new”一個(gè)新的變量--除了true和false操作符。這在重載“++”和“--” 操作符時(shí)尤其顯得重要。也就是說(shuō)我們做在a++時(shí),我們將丟棄原來(lái)的a值,而取代的是新的new出來(lái)的值給a! 值得注意的是e=a++或f=++a中e的值或f的值根本與我們重載的操作符返回值沒(méi)有一點(diǎn)聯(lián)系!它們的值僅僅是在前置和后置的情況下獲得a的舊值或新值而已!前置和后置的行為不難理解。
操作符重載對(duì)返回值和參數(shù)類型有著相當(dāng)嚴(yán)格的要求。一元操作符中只有一個(gè)參數(shù)。操作符“++”和“--”返回值類型和參數(shù)類型必須和聲明該操作符的類型一樣。操作符“+ - ! ~”的參數(shù)類型必須和聲明該操作符的類型一樣,返回值類型可以任意。true和false操作符的參數(shù)類型必須和聲明該操作符的類型一樣,而返回值類型必須為bool,而且必須配對(duì)出現(xiàn)--也就是說(shuō)只聲明其中一個(gè)是不對(duì)的,會(huì)引起編譯錯(cuò)誤。參數(shù)類型的不同會(huì)導(dǎo)致同名的操作符的重載--實(shí)際上這是方法重載的表現(xiàn)。
二元操作符參數(shù)必須為兩個(gè),而且兩個(gè)必須至少有一個(gè)的參數(shù)類型為聲明該操作符的類型。返回值類型可以任意。有三對(duì)操作符也需要必須配對(duì)聲明出現(xiàn),它們是“==”和“!=”,“>”和“<”,“>=”和“<=”。需要注意的是兩個(gè)參數(shù)的類型不同,雖然類型相同但順序不同都會(huì)導(dǎo)致同名的操作符的重載。
轉(zhuǎn)換操作符為不同類型之間提供隱式轉(zhuǎn)換和顯式轉(zhuǎn)換,主要用于方法調(diào)用,轉(zhuǎn)型表達(dá)和賦值操作。轉(zhuǎn)換操作符對(duì)其參數(shù)類型(被轉(zhuǎn)換類型)和返回值類型(轉(zhuǎn)換類型)也有嚴(yán)格的要求。參數(shù)類型和返回值類型不能相同,且兩者之間必須至少有一個(gè)和定義操作符的類型相同。轉(zhuǎn)換操作符必須定義在被轉(zhuǎn)換類型或轉(zhuǎn)換類型任何其中一個(gè)里面。不能對(duì)系統(tǒng)定義過(guò)的轉(zhuǎn)換操作進(jìn)行重新定義。兩個(gè)類型也都不能是object或接口類型,兩者之間不能有直接或間接的繼承關(guān)系--這三種情況系統(tǒng)已經(jīng)默認(rèn)轉(zhuǎn)換。我們來(lái)看一個(gè)例子:
using System;public struct Digit{byte value;public Digit(byte value){if (value < 0 || value > 9)throw new ArgumentException();this.value = value;}public static implicit operator byte(Digit d){return d.value;}public static explicit operator Digit(byte b){return new Digit(b);}}上面的例子提供了Digit類型和byte類型之間的隱式轉(zhuǎn)換和顯式轉(zhuǎn)換。從Digit到byte的轉(zhuǎn)換為隱式轉(zhuǎn)換,轉(zhuǎn)換過(guò)程不會(huì)因?yàn)閬G失任何信息而拋出異常。從byte到Digit的轉(zhuǎn)換為顯式轉(zhuǎn)換,轉(zhuǎn)換過(guò)程有可能因丟失信息而拋出異常。實(shí)際上這也為我們揭示了什么時(shí)候聲明隱式轉(zhuǎn)換,什么時(shí)候聲明顯示轉(zhuǎn)換的設(shè)計(jì)原則。不能對(duì)同一參數(shù)類型同時(shí)聲明隱式轉(zhuǎn)換和顯式轉(zhuǎn)換。隱式轉(zhuǎn)換和顯式轉(zhuǎn)換無(wú)需配對(duì)使用--雖然C#推薦這樣做。
實(shí)際上可以看到,對(duì)于屬性,索引器和操作符這些C#提供給我們的界面操作,都是方法的某種形式的邏輯抽象包裝,它旨在為我們定義的類型的用戶提供一個(gè)友好易用的界面--我們完全可以通過(guò)方法來(lái)實(shí)現(xiàn)它們實(shí)現(xiàn)的功能。理解了這樣的設(shè)計(jì)初衷,我們才會(huì)恰當(dāng),正確地用好這些操作,而不致導(dǎo)致濫用和錯(cuò)用。
轉(zhuǎn)載于:https://www.cnblogs.com/Dicky/archive/2007/05/09/740782.html
總結(jié)
以上是生活随笔為你收集整理的C#锐利体验-第八讲 索引器与操作符重载(转)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 仿淘宝网站的TabPage导航效果
- 下一篇: fatal error C1083: 无