使用 C# 中的索引器和 JavaScript 中訪問對象的屬性是很類似。html
以前瞭解過索引器,當時還把索引器和屬性給記混了, 覺得索引器就是屬性,下面寫下索引器和屬性的區別,以及怎麼使用索引器數據庫
先說明一點,這裏的索引器和數據庫中的索引不同,雖然都是找元素。數組
索引器和屬性的區別:函數
- 屬性和索引器都是函數,可是表現形式不同;(屬性和索引器在代碼的表現形式上和函數不一致,但其本質都是函數,須要經過 ILDASM 來查看,或者使用反射)
- 索引器能夠被重載,而屬性沒有重載這一說法;(索引器的重載即方括號中的類型不一樣)
- 索引器不能聲明爲static,而屬性能夠;(索引器之因此不能聲明爲 static,由於其自身攜帶 this 關鍵字,須要被對象調用)
還有一點就是索引很像數組,它容許一個對象能夠像數組同樣被中括號 [] 索引,可是和數組有區別,具體有:工具
- 數組的角標只能是數字,而索引器的角標能夠是數字也能夠是引用類型;
- 數組是一個引用類型的變量,而索引器是一個函數;
我在代碼中不多本身定義索引器,可是我卻常常在用它,那是由於系統自定義了不少索引器,好比 ADO.NET 中對於 DataTable 和 DataRow 等類的各類遍歷,查找,不少地方就是用的索引器,好比下面這篇博客中的代碼就使用了不少系統自定義的索引器:this
DataTable的AcceptChanges()方法和DataRow的RowState屬性 (其中索引器的使用都以及註明,)spa
那咱們如何自定索引器? 回到這篇博客第一句話,我曾經把索引器和屬性弄混過,那就說明他倆很像,看下代碼看是否是很像:3d
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Dynamic;
//這裏的代碼時參照自http://www.cnblogs.com/ArmyShen/archive/2012/08/27/2659405.html的代碼片斷 namespace Demo1 { public class IndexerClass { private string[] name = new string[2]; //索引器必須以this關鍵字定義,其實這個this就是類實例化以後的對象 public string this[int index] { //實現索引器的get方法 get { if (index >= 0 && index < 2) { return name[index]; } return null; } //實現索引器的set方法 set { if (index >= 0 && index < 2) { name[index] = value; } } } } class Program { static void Main(string[] args) { //索引器的使用 IndexerClass Indexer = new IndexerClass(); //「=」號右邊對索引器賦值,其實就是調用其set方法 Indexer[0] = "張三"; Indexer[1] = "李四"; //輸出索引器的值,其實就是調用其get方法 Console.WriteLine(Indexer[0]); Console.WriteLine(Indexer[1]); } } }
乍一眼看上去,感受和屬性差很少了, 可是仔細一看,索引器沒有名稱,有對方括號,並且多了 this 關鍵字,看到這裏的 this 的特殊用法又讓我想到了擴展方法中的 this的特殊位置。code
上面再講索引和屬性的區別時,說到了索引其實也是函數,證實索引器是函數,我在上面的的代碼中加入了一個普通方法 Add,htm
public class IndexerClass { public string[] strArr = new string[2]; //一個屬性 public int Age { get; set; } //一個方法 public int Add(int a,int b) { return a + b; } //一個索引器 public string this[int index] { get { if (index < 2 && index >= 0) return strArr[index]; return null; } set { if (index >= 0 && index < 2) { strArr[index] = value; } } } }
咱們將程序從新編譯一下,而後使用 ILDASM 工具查看下編譯出來的 .exe 文件,以下圖:
從中咱們能夠看到一個 Add 的方法,這個小圖標表明着方法,一共有 6 個方法,其中 .ctor 暫時無論,Add 方法是咱們本身寫的,
get_Age : int32(),這個方法是屬性 age 的讀方法,對應的還有個寫方法;
還剩下兩個就是索引器生成的方法了,get_Item:string(int32) 和 set_Item : void(int32) ;
還有這樣的圖標,這個圖標是表明着字段,上面的兩個字段是自動生成的,這也是 C#中的語法糖了,不用聲明字段,只用聲明屬性,編譯器會自動生成相應字段。
關於 C# 的語法糖能夠點擊這篇博客【 C# 中的語法糖 】
關於 ILDASM 的安裝與使用能夠點擊這篇博客【ILDASM 的添加和使用】
以字符串爲角標, 這點就和數組不同了:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Dynamic; using System.Collections; //這裏的代碼時參照自http://www.cnblogs.com/ArmyShen/archive/2012/08/27/2659405.html的代碼片斷 namespace Demo1 { public class IndexerClass { //用string做爲索引器下標的時候,要用Hashtable private Hashtable name = new Hashtable(); //索引器必須以this關鍵字定義,其實這個this就是類實例化以後的對象 public string this[string index] { get { return name[index].ToString(); } set { name.Add(index, value); } } } class Program { static void Main(string[] args) { IndexerClass Indexer = new IndexerClass(); Indexer["A0001"] = "張三"; Indexer["A0002"] = "李四"; Console.WriteLine(Indexer["A0001"]); Console.WriteLine(Indexer["A0002"]); } } }
索引器的重載,這點就是屬性的不一樣:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Dynamic; using System.Collections; //這裏的代碼時參照自http://www.cnblogs.com/ArmyShen/archive/2012/08/27/2659405.html的代碼片斷 namespace Demo1 { public class IndexerClass { private Hashtable name = new Hashtable(); //1:經過key存取Values public string this[int index] { get { return name[index].ToString(); } set { name.Add(index, value); } } //2:經過Values存取key public int this[string aName] { get { //Hashtable中實際存放的是DictionaryEntry(字典)類型,若是要遍歷一個Hashtable,就須要使用到DictionaryEntry foreach (DictionaryEntry d in name) { if (d.Value.ToString() == aName) { return Convert.ToInt32(d.Key); } } return -1; } set { name.Add(value, aName); } } } class Program { static void Main(string[] args) { IndexerClass Indexer = new IndexerClass(); //第一種索引器的使用 Indexer[1] = "張三";//set訪問器的使用 Indexer[2] = "李四"; Console.WriteLine("編號爲1的名字:" + Indexer[1]);//get訪問器的使用 Console.WriteLine("編號爲2的名字:" + Indexer[2]); Console.WriteLine(); //第二種索引器的使用 Console.WriteLine("張三的編號是:" + Indexer["張三"]);//get訪問器的使用 Console.WriteLine("李四的編號是:" + Indexer["李四"]); Indexer["王五"] = 3;//set訪問器的使用 Console.WriteLine("王五的編號是:" + Indexer["王五"]); } } }
具備多個參數的索引器:
using System; using System.Collections; //這裏的代碼時參照自http://www.cnblogs.com/ArmyShen/archive/2012/08/27/2659405.html的代碼片斷 namespace Demo1 { /// <summary> /// 入職信息類 /// </summary> public class EntrantInfo { //姓名、編號、部門 public string Name { get; set; } public int Num { get; set; } public string Department { get; set; } } /// <summary> /// 聲明一個類EntrantInfo的索引器 /// </summary> public class IndexerForEntrantInfo { private ArrayList ArrLst;//用於存放EntrantInfo類 public IndexerForEntrantInfo() { ArrLst = new ArrayList(); } /// <summary> /// 聲明一個索引器:以名字和編號查找存取部門信息 /// </summary> /// <param name="name"></param> /// <param name="num"></param> /// <returns></returns> public string this[string name, int num] { get { foreach (EntrantInfo en in ArrLst) { if (en.Name == name && en.Num == num) { return en.Department; } } return null; } set { ArrLst.Add(new EntrantInfo() { Name = name, Num= num, Department = value }); } } /// <summary> /// 聲明一個索引器:以編號查找名字和部門 /// </summary> /// <param name="num"></param> /// <returns></returns> public ArrayList this[int num] { get { ArrayList temp = new ArrayList(); foreach (EntrantInfo en in ArrLst) { if (en.Num == num) { temp.Add(en); } } return temp; } } //還能夠聲明多個版本的索引器... } class Program { static void Main(string[] args) { IndexerForEntrantInfo Info = new IndexerForEntrantInfo(); //this[string name, int num]的使用 Info["張三", 101] = "人事部"; Info["李四", 102] = "行政部"; Console.WriteLine(Info["張三", 101]); Console.WriteLine(Info["李四", 102]); Console.WriteLine(); //this[int num]的使用 foreach (EntrantInfo en in Info[102]) { Console.WriteLine(en.Name); Console.WriteLine(en.Department); } } } }