概述
此部分內容參考自MSDN檔案
-
使用索引器可以用類似于陣列的方式為物件建立索引,
-
get取值函式回傳值,set取值函式分配值, -
this關鍵字用于定義索引器, -
value關鍵字用于定義set索引器所賦的值, -
索引器不必根據整數值進行索引;由你決定如何定義特定的查找機制,
-
索引器可被多載,
-
索引器可以有多個形參,例如當訪問二維陣列時,
我對索引器的理解就是,他是一個讀寫自定義類中的資料集合的介面,連接自定義類中的資料集合,并可對其進行讀寫操作
通過該介面簡化或者豐富對自定義類中資料集合的操作方式
索引器實際上相當于一個方法,支持多個及多種型別的引數,不同的是,其回傳值不可為
void,并且索引器除可傳入引數外,還可對其進行賦值,即it[0] = "測驗資料0"創建索引器時,其回傳值型別亦為其
value關鍵字所使用的型別,即定義了回傳值型別的同時,也定義了其可接受的值型別
索引器使用要素
創建索引器時有幾部分內容是必須的:
-
必須先創建索引器所需要的容器(我把它稱為容器,暫時還沒看到有對它的具體定義)
-
創建索引器需要使用
this關鍵字 -
索引器中必須要包含
get和set訪問器,在C#7.0可以使用運算式主體(=>)簡化 -
在使用運算式主體成員實作索引器時,必須額外提供容器的修改介面,因為通過運算式主體實作的索引器是不包含
set關鍵字的
單引數索引器
此索引器使用簡單的string陣列作為容器,此索引器使用int型別的i進行索引,回傳值為string型別,
class SampleIndxer
{
//可供索引器使用的容器,暫用陣列
private string[] sampleStrArr = new string[10];
//創建索引器
public string this[int i]
{
get { return sampleStrArr[i]; }
set { sampleStrArr[i] = value; }
}
}
class Test
{
public static void test()
{
//簡單索引器測驗
SampleIndxer it = new SampleIndxer();
it[0] = "測驗資料0";
it[1] = "測驗資料1";
Console.WriteLine("it[0]:" + it[0]);
Console.WriteLine("it[1]:" + it[1]);
Console.ReadLine();
}
}
索引器中同時也可以使用泛型作為引數
class SampleGenericIndexer<T>
{
//可供索引器使用的主體變數,暫用泛型陣列代替
private T[] sampleGenericStrArr = new T[10];
public T this[int i]
{
get { return sampleGenericStrArr[i]; }
set { sampleGenericStrArr[i] = value; }
}
}
class Test
{
public static void test()
{
//泛型索引器測驗
SampleGenericIndexer<string> it = new SampleGenericIndexer<string>();
it[0] = "測驗資料0";
it[1] = "測驗資料1";
Console.WriteLine("it[0]:" + it[0]);
Console.WriteLine("it[1]:" + it[1]);
Console.ReadLine();
}
}
在C#7.0之后可以通過運算式主體實作索引器,需要注意的是,通過運算式主體實作索引器時,必須提供資料修改的介面,因為通過運算式主體實作索引時僅提供了get訪問器,并未提供set訪問器,或者將容器的可訪問性設定為使用該類的地方可以訪問,直接對容器進行資料操作,僅使用索引器進行資料的讀取,
class ExpressionBodyIndexer<T>
{
//可供索引器使用的主體變數,暫用泛型陣列代替
private T[] expressionBodyStrArr = new T[10];
//標記當前索引器的中已初始化資料的索引位置
int nextIndex = 0;
// 使用運算式主體(ExpressionBody)定義簡化定義索引器
public T this[int i] => expressionBodyStrArr[i];
/// <summary>
/// 運算式主體方式定義的索引器無法通過索引值設定其中的值
/// 因為此狀態下,索引器的資料為只讀狀態
/// 必須向外提供賦值的方法
/// </summary>
/// <param name="value"></param>
public void Add(T value)
{
if(nextIndex >= expressionBodyStrArr.Length)
{
throw new IndexOutOfRangeException($"當前集合資料已滿,共{expressionBodyStrArr.Length}組資料");
}
expressionBodyStrArr[nextIndex++] = value;
}
}
class Test
{
public static void test()
{
//泛型索引器測驗
ExpressionBodyIndexer<string> it = new ExpressionBodyIndexer<string>();
//此條件下不可通過it[0]索引方式進行資料添加,因為他是只讀的
//必須通過提供的Add方法添加資料
it.Add("測驗資料0");
it.Add("測驗資料1");
it.Add("測驗資料2");
Console.WriteLine("it[0]:" + it[0]);
Console.WriteLine("it[1]:" + it[1]);
Console.WriteLine("it[2]:" + it[2]);
Console.ReadLine();
}
}
索引器既然是可以簡化或者豐富對自定義類中資料集合的操作方式,那么自然也可以使用稍微復雜點的資料集合作為索引器的容器,本例中使用Dictionary作為容器,
class VariableLengthIndexer
{
/// <summary>
/// 可供索引器使用的容器,此處使用Dictionary代替,
/// 實作使用string型別資料當作索引器的指標,同時實作索引器的可變長度
/// </summary>
private Dictionary<string, string> dic = new Dictionary<string, string>();
/// <summary>
/// 使用運算式主體創建索引器
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public string this[string s] => dic[s];
public void Add(string key,string value)
{
if (dic.ContainsKey(key))
{
dic[key] = value;
}
else
{
dic.Add(key, value);
}
}
}
class Test
{
public static void test()
{
//泛型索引器測驗
VariableLengthIndexer it = new VariableLengthIndexer();
//此條件下不可通過it[0]索引方式進行資料添加,因為他是只讀的
//必須通過提供的Add方法添加資料
it.Add("資料0", "測驗資料0");
it.Add("資料1", "測驗資料1");
it.Add("資料2", "測驗資料2");
Console.WriteLine("it[資料1]:" + it["資料1"]);
Console.WriteLine("it[資料2]:" + it["資料2"]);
Console.WriteLine("it[資料3]:" + it["資料3"]);
Console.ReadLine();
}
}
前面的幾個例子中,僅僅是對于索引器的認識,實際作業中并沒有使用價值,因為所作的操作完全可以使用 .NET 中預定義的資料集合完成,個人覺得C#7.0之后提供的運算式主體實際作用并不大,甚至沒有必要,個人認為索引器最大價值存在于get和set訪問器中對于資料操作的自定義處理,可以在訪問器中對資料進行修正或者過濾,這才是其比較好的價值體現,
通過在索引器中對資料處理做封裝,可以簡化平常大部分的操作,此類也可根據實際情況嵌入到資料庫訪問物體類中,
/// <summary>
/// 本實體通過考試成績的處理演示索引器對資料處理的程序
/// </summary>
class TestScore
{
private Dictionary<string, int> scores = new Dictionary<string, int>();
public string this[string s]
{
get
{
if (!scores.ContainsKey(s))
{
return $"非常抱歉,{s}的成績尚未錄入";
}
switch (scores[s])
{
case 10:
case 20:
case 30:
case 40:
case 50:
return $"很遺憾,{s}不及格,分數僅為{scores[s]}";
case 60:
case 70:
return $"考的不錯,{s}已及格,分數為{scores[s]}";
case 80:
case 90:
return $"成績優秀,{s}成績優秀,分數為{scores[s]}";
case 100:
return $"非常優秀,{s}獲取滿分{scores[s]}分";
default:
return $"{s}的成績可能存在例外,分數為{scores[s]}";
}
}
set
{
if (int.TryParse(value, out int v))
{
//對分數做四舍五入處理
v = (int)Math.Round(v * 0.1) * 10;
if (!scores.ContainsKey(s))
{
scores.Add(s, v);
}
else
{
scores[s] = v;
}
}
}
}
}
class Test
{
public static void test()
{
TestScore ts = new TestScore();
ts["張三"] = "23";
ts["李四"] = "54";
ts["王二"] = "66";
ts["麻子"] = "89";
ts["王朝"] = "100";
ts["馬漢"] = "5";
ts["老王"] = "";
Console.WriteLine(ts["張三"]);
Console.WriteLine(ts["李四"]);
Console.WriteLine(ts["王二"]);
Console.WriteLine(ts["麻子"]);
Console.WriteLine(ts["王朝"]);
Console.WriteLine(ts["馬漢"]);
Console.WriteLine(ts["老王"]);
Console.ReadLine();
}
}

多引數索引器
前面通過單引數所以其的實作分析了索引器的使用方式即可能的使用范圍,下面進行下簡單的拓展,分析多引數索引器的使用方式,依舊使用上面分數的例子做演示,
struct Student
{
public string Name;
public string Classes;
public string Grade;
public int Score;
public override string ToString()
{
return $"{this.Grade}\t{this.Classes}\t{this.Name}\t{this.Score}";
}
}
public class ArrayList1 : ArrayList
{
public override bool Contains(object item)
{
if (item.GetType().ToString() == "Student")
{
foreach (var a in this)
{
if (a.GetType().ToString() == "Student")
{
var s1 = (Student)a;
var s2 = (Student)item;
if (s1.Name == s2.Name && s1.Classes == s2.Classes && s1.Grade == s2.Grade)
{
return true;
}
return false;
}
}
}
return base.Contains(item);
}
}
class TestScore
{
public ArrayList1 ArrList = new ArrayList1();
public string this[string name, string grade, string classes]
{
get
{
string rtn = "";
foreach (Student a in ArrList)
{
if (a.Name == name && a.Classes == classes && a.Grade == grade)
{
switch (a.Score)
{
case 10:
case 20:
case 30:
case 40:
case 50:
rtn = $"很遺憾,{name}不及格,分數僅為{a.Score}";
break;
case 60:
case 70:
rtn = $"考的不錯,{name}已及格,分數為{a.Score}";
break;
case 80:
case 90:
rtn = $"成績優秀,{name}成績優秀,分數為{a.Score}";
break;
case 100:
rtn = $"非常優秀,{name}獲取滿分{a.Score}分";
break;
default:
rtn = $"{name}的成績可能存在例外,分數為{a.Score}";
break;
}
}
}
if (rtn == "")
{
return $"非常抱歉,{name}的成績尚未錄入";
}
return rtn;
}
set
{
if (int.TryParse(value, out int v))
{
//對分數做四舍五入處理
v = (int)Math.Round(v * 0.1) * 10;
Student st = new Student
{
Name = name,
Grade = grade,
Classes = classes,
Score = v
};
//重復項,不再插入,避免查找時出現重復
if (!ArrList.Contains(st))
{
ArrList.Add(st);
}
}
}
}
}
class Test
{
public static void test()
{
TestScore ts = new TestScore();
ts["張三", "三年級", "二班"] = "23";
ts["李四", "三年級", "二班"] = "54";
ts["王二", "三年級", "二班"] = "66";
ts["麻子", "三年級", "二班"] = "89";
ts["王朝", "三年級", "二班"] = "100";
ts["馬漢", "三年級", "二班"] = "5";
ts["老王", "三年級", "二班"] = "";
Console.WriteLine("查看存入的資料:");
Console.WriteLine($"共存入了:{ts.ArrList.Count}組資料");
Console.WriteLine();
//不使用索引器,直接訪問實體中的容器
foreach (Student s in ts.ArrList)
{
Console.WriteLine(s.ToString());
}
Console.WriteLine();
Console.WriteLine(ts["張三", "三年級", "二班"]);
Console.WriteLine(ts["李四", "三年級", "二班"]);
Console.WriteLine(ts["王二", "三年級", "二班"]);
Console.WriteLine(ts["麻子", "三年級", "二班"]);
Console.WriteLine(ts["王朝", "三年級", "二班"]);
Console.WriteLine(ts["馬漢", "三年級", "二班"]);
Console.WriteLine(ts["老王", "三年級", "二班"]);
Console.ReadLine();
}
}

同時二維陣列中多個引數的實作方式,同樣也支持二維陣列
public string[,] sampleStrArr = new string[10,10];
public string this[int x,int y]
{
get { return sampleStrArr[x, y]; }
set { sampleStrArr[x, y] = value; }
}
public static void test()
{
SampleIndxer it = new SampleIndxer();
it[0, 0] = "測驗資料0,0";
it[0, 1] = "測驗資料0,1";
it[1, 1] = "測驗資料1,1";
it[1, 2] = "測驗資料1,2";
it[3, 3] = "測驗資料3,3";
Console.WriteLine("it[0,0]:" + it[0, 0]);
Console.WriteLine("it[0,1]:" + it[0, 1]);
Console.WriteLine("it[1,1]:" + it[1, 1]);
Console.WriteLine("it[1,2]:" + it[1, 2]);
Console.WriteLine("it[3,3]:" + it[3, 3]);
Console.ReadLine();
}
索引器的多載
前面說過,索引器相當于一個方法,他們同樣都支持多載,與方法不同的是,索引器沒有獨立的名稱,只能通過回傳值的不同和引數的不同來區分不同的簽名,從而實作多載,
class VariableLengthIndexer
{
private Dictionary<string, int> dic = new Dictionary<string, int>();
//通過Key,查找Value
public int this[string s]
{
get { return dic[s]; }
}
//通過Value查找Key
public string this[int num]
{
get { return dic.Where(x => x.Value =https://www.cnblogs.com/Hope-forever/p/= num).Last().Key; }
}
//通過Value查找Key,添加無效引數num1演示多載
public string this[int num, int num1]
{
get { return dic.Where(x => x.Value == num).Last().Key; }
}
public void Add(string key, int value)
{
if (dic.ContainsKey(key))
{
dic[key] = value;
}
else
{
dic.Add(key, value);
}
}
}
class Test
{
public static void test()
{
//泛型索引器測驗
VariableLengthIndexer it = new VariableLengthIndexer();
it.Add("測驗資料1", 1);
it.Add("測驗資料2", 2);
it.Add("測驗資料3", 3);
it.Add("測驗資料4", 4);
//通過Key查找Value
Console.WriteLine("通過Key查找Value");
Console.WriteLine("Key:測驗資料1,Value:" + it["測驗資料1"]);
Console.WriteLine("Key:測驗資料2,Value:" + it["測驗資料2"]);
Console.WriteLine("Key:測驗資料3,Value:" + it["測驗資料3"]);
Console.WriteLine("Key:測驗資料4,Value:" + it["測驗資料4"]);
//通過Value查找Key
Console.WriteLine("通過Value查找Key");
Console.WriteLine("Value:1,Key:" + it[1]);
Console.WriteLine("Value:2,Key:" + it[2]);
Console.WriteLine("Value:3,Key:" + it[3]);
Console.WriteLine("Value:4,Key:" + it[4]);
//通過Value查找Key,并添加無效引數傳入
Console.WriteLine("通過Value查找Key,并添加無效引數傳入");
Console.WriteLine("Value:1,Key:" + it[1, 1]);
Console.WriteLine("Value:2,Key:" + it[2, 2]);
Console.WriteLine("Value:3,Key:" + it[3, 3]);
Console.WriteLine("Value:4,Key:" + it[4, 4]);
Console.ReadLine();
}
}

參考文獻:
1 C# 中常用的索引器 https://www.cnblogs.com/daimajun/p/6819081.html
2 索引器(C# 編程指南)https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/indexers/
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/86488.html
標籤:C#
