16 使用索引器

1.什麼是索引器

屬性可被視爲一種智能字段:類似地,索引器可被視爲一種智能數組(索引器本質上是“有參屬性”:而上一片博客所說的普通屬性是“無參屬性”。 “索引器”只是C#對“有參屬性”的叫法。)。屬性封裝了類中的一個值,而索引器封裝了一組值。使用索引器時,語法和使用數組完全相同。

 

1.1不用索引器的例子

例如:以下表達式使用左位移(<<)和按位AND(&)操作符判斷在名爲bits的一個int中,位於位置5(右數第6位)的二進制是0還是1:

(bits & (1<<5))!= 0

如果bits變量包含十進制值42,即二進制00101010。 十進制值1的二進制是0000001.所以表達式1 << 5的結果是00100000。右數第6位是1。因此,表達式bits& (1 << 5)相當於00101010 & 00100000.結果是00100000(非零)。 如果bits變量包含65,或者01000001,那麼表達式01000001 &01000000結果是00000000,A即十進制0。

雖然這已經是一個比較複雜的表達式,但和下而這個表達式(使用複合賦值操作符&=將位置6的位設爲0)相比,其複雜性又顯得微不足道了:

bits &= ~(1 <<  5)

類似地,要將位置6的位設爲1,可用按位OR(|)操作符。下面這個複雜的表達式以複合賦值操作符|=爲基礎:

bits |= (1<< 5)

這些例子的通病在於,雖然能起作用,但不能清楚表示爲什麼要這樣寫,我們搞不清楚它們是如何工作的。過於複雜,解決方案很低級。也就是說,無法對要解決的問題進行

抽象,會造成難以維護的代碼。

 

1.2同一個例子改用索引器

現在,暫停對前面的低級解決方案的思索,將重點放在問題的本質上。現在需要的是將int作爲一個由32個二進制位構成的數組使用,而不是作爲int使用。所以,解決問題的最佳方案是將int想象成包含32位的一個數組!也就是說,如果bits是int,那麼爲了訪問右數第6個二進制位,我們想這樣寫(記住索引從0開始):

bits[5]

爲了將右數第4位設爲true,我們希望能像下面這樣寫:

bits[3] = true;

遺憾的是,不能爲int使用方括號記號法。這種記號法僅適合數組或行爲與數組相似的類型。所以,解決方案是新建一.種類型,它在行爲、外觀和用法上都類似於bool數組,但它用int實現。需爲此定義一個索引器。假定新類型名爲IntBits. IntBits 將包含一個int值(在構造器中初始化),但我們要將IntBits作爲由bool變量構成的數組使用:

struct IntBits
{
    private int bits;
    publlc IntBits(int initialBitValue)

    {
        bits.initialBitValue;
    }

    //在這裏寫索引器

}

提示:由於IntBits很小,是輕量級的,所以有必要把它作爲結構而不是類來創建。

 

定義索引器要採取一種 兼具屬性和數組特徵的記號法。索引器由this 關鍵字引入,this之前指定索引器的返回值類型。在this之後的方括號中,指定作爲索引器的索引使用的值的類型。IntBits結構的索引器使用整數作爲索引類型,返回bool值,如下所示:

struct IntBits
{

    public bool this [ int index ]
    {

        get
        {
            return (bits & (1 << index)) != 0;
        }

        set{
            if (value)    //如果value爲true,就將指定的位設爲1(開):否則設爲B(關)
                bits |=  (1 << index);
            else
                bits &=  ~(1 << index);
        }
    }

}

注意一下幾點:
(1)索引器不是方法——沒有一對包含參數的圓括號,但有一對指定了索引的方括號。索引指定要訪問哪一個元素。

(2)所有索引都使用this關鍵字取代方法名。每個類或結構只允許定義一個索引器(雖然可以重載並有多個實現),而且總是命名爲this。

(3)和屬性一樣,索引器也包含get和set這兩個訪問器。本例的get和set訪問器包含前面討論過的按位表達式。

(4)索引器聲明中指定的index將調用索引器時指定的索引值來填充。get和set訪問器方法可以讀取這個實參,判斷應訪問哪一個元素。

 

聲明好索引器後,就可用IntBits(而非int)類型的變量並使用方括號記號法:

int adapted = 126;   // 126的二進制形式是01111110

IntBits bits = new IntBits(adapted);

bool peek = bits[6];  //獲取索引位置6的bool值:應該是true(1)

bits[0] = true; //將家引0的位設爲true(1)

bits[3] = false;//將索引3的位設爲false(0)

//現在bits的值是01110111或十進制119

 

1.3理解索引器的訪問器

讀取索引器時,編譯器自動將數組風格的代碼轉換成對那個索引器的get訪問器的調用。例如,以下代碼轉換成對bits的get訪問器的調用,index 參數值設爲6;

bool peek = bits[6];

類似地,向索引器寫入時,編譯器將數組風格的代碼轉換成對索引器的set訪問器的調用,並將index參數設爲方括號中指定的值。例如:

bits[3] = true;

該語句將轉換成對bits的set訪問器的調用,index 值設爲3.和普通屬性一一樣,向索引器寫入的值(本例是true)是通過value關鍵字來訪問的。value 的類型與索引器本身的類型相同(本例是bool)。

還可在同時讀取和寫入的情況下使用索引器。這種情況要同時用到get和set訪問器。例如,以下語句使用XOR操作符(^)反轉bits變量索引6的二進制位:

bits[6] = true;

它自動轉換成以下形式:

bits[6] = bits[6] ^ true;

上述代碼之所以能奏效,是由於索引器同時聲明瞭get 和set訪問器。

 

1.4對比索引器和數組

(1)索引器能使用非數值下標:

public int this [string name]{…}//合法

 

(2)索引 器能重載(這和方法相似),數組則不能:

public Name this [ PhoneNumber number ] { ... }

public PhoneNumber this [ Name name ] { ... }

(3)索引器不能作爲 ref或out參數使用,數組元素則能:

IntBits bits; // bits包含一個索引器

Method(ref bits[1]); // 編譯時錯誤

2.接口中的索引器

可在接口中聲明索引器。爲此,需要指定get以及/或者set關鍵字。但是,get和set訪問器的主體要替換成分號。實現該接口的任何類或結構都必須實現接口所聲明的索引器的訪問器,例如:

interface TRawInt
{
    bool this [ int index 1 { get; set; }
}



struct RawInt : IRawInt
{
    …
    public bool this [ int index ]
    {
        get{...}
        set{...}
    }
    …

}

在類中實現接口要求的索引器時,可將索引器的實現聲明爲virtual,從而允許派生類重寫get和set訪問器。例如,前面的例子可以改寫成以下形式:

class RawInt : IRawInt
{

    …
    public virtual bool this [ int index ]    
    {
        get {...}
        set {... }
    }
    …

}

還可以附加接口名稱作爲前綴,通過“顯式接口實現”語法(參見之前的博客)來實現索引器。索引器的顯式實現是非公共和非虛的(所以不能被重寫),例如:

struct RawInt : TRawInt
{
    …
    bool IRawInt.this [ int index ]
    {
        get{...}
        set{...}
    }
    …
}

參考書籍:《Visual C#從入門到精通》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章