2014年6月1日 星期日

C# - Indexer 索引子


C#中的索引子 (Indexer)是一個相當直覺好用的設計,讓開發者可以在建立類別時,如同存取陣列一樣的對內部資料做存取,用來封裝內部集合或陣列。

索引子 (Indexer) 允許使用與陣列相同的方式來索引類別 (Class) 或結構 (Struct) 的執行個體。 索引子除了其存取子 (Accessor) 會使用參數以外,其餘特性都與屬性相似。
在下列範例中,將定義一個泛型類別,並提供簡單的 get set 存取子方法來做為指派與擷取值的方式。 Program 類別會建立此類別的執行個體以儲存字串。

class SampleCollection<T>
{

    private T[] arr = new T[100];
    public T this[int i]
    {
        get
        {
            return arr[i];
        }
        set
        {
            arr[i] = value;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        SampleCollection<string> stringCollection = new SampleCollection<string>();

        stringCollection[0] = "Hello, Indexer";
        System.Console.WriteLine(stringCollection[0]);
    }

}


索引子概觀

  • 索引子可讓物件以類似於陣列的方式進行索引。
  • get 存取子會傳回一個值。 set 存取子會指定一個值。
  • this 關鍵字的用途為定義索引子。
  • value 關鍵字是用來定義 set 索引子所指定的值。
  • 索引子不需要以整數值來索引;您可以決定如何定義特定的查詢機制。
  • 索引子可以多載。
  • 索引子可以具有一個以上的型式參數,例如,在存取二維陣列時便是如此。

例如有10個村里要發佈成為土石流警戒區,希望將這些村里歸類到所屬的鄉鎮,以方便可以快速的取出鄉鎮及村里統計及詳細資訊,最重要的是希望用一種更直覺的方式來實現這樣的設計。

實作方法設計 County Town 兩個類別:

County 中使用 Indexer,使用者可以指定鄉鎮就可以存取內部的鄉鎮清單(TownItem),在 getset存取運算子中,使用 Lambda 來判斷鄉鎮是否存在 TownItem 內:

public class County
{

     public string Name{set;get;}// 縣市名稱
     private List TownItem = new List();// 鄉鎮清單

     //鄉鎮名稱
     public Town this[String town]
     {
        get
        {
            //透過 Lambda 判斷鄉鎮是否存在 TownItem
            Town pTown = this.TownItem.Find(c => (c.Name == town));
            if (pTown == null)
            {
                TownItem.Add(new Town() { Name = town });
                pTown = this.TownItem.Find(c => (c.Name == town));
            }
            return pTown;
        }

        set
        {
            Town pTown = this.TownItem.Find(c => (c.Name == town));
            if (pTown == null)
            {
                TownItem.Add(new Town() { Name = town });
                pTown = this.TownItem.Find(c => (c.Name == town));
            }
            pTown = value;
        }
     }
}

Town 類別就只是一個單純的資料類別,負責用來記錄資料
public class Town
{
     public String Name { setget; }
     private List VillItems = new List();

     public void AddVill(String VillName)
     {
        VillItems.Add(VillName);
     }

     public List Items
     {
        get { return this.VillItems;}//取回村里清單
     }
}

使用Indexer的方式,就如同下面的範例:

County pCounty = new County() { Name = "台中市" };

//分別加入幾個村里到行政區內
pCounty["西屯區"].AddVill("至善里");
pCounty["西屯區"].AddVill("逢甲里");
pCounty["北屯區"].AddVill("北屯里");
pCounty["北屯區"].AddVill("三合里");

//取出在西屯區的村里資料
foreach (String s in pCounty["西屯區"].Items)
{
     Console.WriteLine(s);
}

這跟array 很像但是不同的是他可以透過string 的方式去找尋某集合的個體,每一個縣市都是被封裝起來,等需要時,再透過string索引出來使用。

下面是一個搜尋使用者的類別方法:

public class User
{
    private List<string> AllUser { getset; }//所有User
    public User(int num)//Ctor
    {
        AllUser = new List<string>();//Init
        //使用者放入多少數字我就建立多少資料進入AllUser
        for (int i = 0; i <= num; i++)
        {
            AllUser.Add("使用者" + i);
        }
    }

    //Indexer 數字
    public string this[int index]
    {
        get
        {
            if (index < AllUser.Count)
            {
                return AllUser[index];
            }
            else
            {
                return "Error";
            }
        }

        set
        {
            AllUser[index] = value;
        }
    }

     //Indexer Overloading 文字
    public string this[string indexValue]
    {
        get
        {
            foreach (var user in AllUser)
            {
                if (user == indexValue)
                {
                    return AllUser.IndexOf(user).ToString();
                }
            }
            return "No User";
        }
    }

}

在範例中AllUser被封裝起來,但允許使用者透過indexer 透過數字去找尋AllUser
輸入文字之後,就會回覆該User所在位置。它的概念很像Json的存取方式,但又結合了Array對每個位址記憶的存放,非常的方便。


-雲遊山水為知已逍遙一生而忘齡- 電腦神手