此部份内容引用自MSDN文档html
使用索引器能够用相似于数组的方式为对象创建索引。数据库
get
取值函数返回值。 set
取值函数分配值。express
this
关键字用于定义索引器。编程
value
关键字用于定义 set
索引器所赋的值。数组
索引器没必要根据整数值进行索引;由你决定如何定义特定的查找机制。ide
索引器可被重载。函数
索引器能够有多个形参,例如当访问二维数组时。测试
我对索引器的理解就是,他是一个读写自定义类中的数据集合的接口,链接自定义类中的数据集合,并可对其进行读写操做ui
经过该接口简化或者丰富对自定义类中数据集合的操做方式this
索引器实际上至关于一个方法,支持多个及多种类型的参数,不一样的是,其返回值不可为
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/