概述 此部分內容引用自 "MSDN文檔" 使用索引器可以用類似於數組的方式為對象建立索引。 取值函數返回值。 取值函數分配值。 關鍵字用於定義索引器。 關鍵字用於定義 索引器所賦的值。 索引器不必根據整數值進行索引;由你決定如何定義特定的查找機制。 索引器可被重載。 索引器可以有多個形參,例如當訪問 ...
概述
此部分內容引用自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 == 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/