概述

此部分内容引用自MSDN文档

  • 使用索引器可以用类似于数组的方式为对象建立索引。

  • get 取值函数返回值。 set 取值函数分配值。

  • this 关键字用于定义索引器。

  • value 关键字用于定义 set 索引器所赋的值。

  • 索引器不必根据整数值进行索引;由你决定如何定义特定的查找机制。

  • 索引器可被重载。

  • 索引器可以有多个形参,例如当访问二维数组时。

我对索引器的理解就是,他是一个读写自定义类中的数据集合的接口,连接自定义类中的数据集合,并可对其进行读写操作

通过该接口简化或者丰富对自定义类中数据集合的操作方式

索引器实际上相当于一个方法,支持多个及多种类型的参数,不同的是,其返回值不可为void,并且索引器除可传入参数外,还可对其进行赋值,即it[0] = "测试数据0"

创建索引器时,其返回值类型亦为其value关键字所使用的类型,即定义了返回值类型的同时,也定义了其可接受的值类型

索引器使用要素

创建索引器时有几部分内容是必须的:

  1. 必须先创建索引器所需要的容器(我把它称为容器,暂时还没看到有对它的具体定义)

  2. 创建索引器需要使用this关键字

  3. 索引器中必须要包含getset访问器,在C#7.0可以使用表达式主体(=>)简化

  4. 在使用表达式主体成员实现索引器时,必须额外提供容器的修改接口,因为通过表达式主体实现的索引器是不包含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之后提供的表达式主体实际作用并不大,甚至没有必要。个人认为索引器最大价值存在于getset访问器中对于数据操作的自定义处理,可以在访问器中对数据进行修正或者过滤,这才是其比较好的价值体现。

通过在索引器中对数据处理做封装,可以简化平常大部分的操作,此类也可根据实际情况嵌入到数据库访问实体类中。

/// <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/

C# 索引器的理解和使用的更多相关文章

  1. C#中的索引器的简单理解和用法

    索引器是一种特殊的类成员,它能够让对象以类似数组的方式来存取,使程序看起来更为直观,更容易编写. 1.索引器的定义 C#中的类成员可以是任意类型,包括数组和集合.当一个类包含了数组和集合成员时,索引器 ...

  2. C#索引器理解

    C#索引器介绍举例 索引器允许类或者结构的实例按照与数组相同的方式进行索引取值,索引器与属性类似,不同的是索引器的访问是带参的. 索引器和数组比较: (1)索引器的索引值(Index)类型不受限制 ( ...

  3. C#基础回顾(三)—索引器、委托、反射

    一.前言                                                                                       ------人生路 ...

  4. 描述一下C#中索引器的实现过程,是否只能根据数字进行索引?

    不是.可以用任意类型. 索引器是一种特殊的类成员,它能够让对象以类似数组的方式来存取,使程序看起来更为直观,更容易编写. 1.索引器的定义 C#中的类成员可以是任意类型,包括数组和集合.当一个类包含了 ...

  5. C# 索引器简介

    索引器是一种特殊的类成员,它能够让对象以类似数组的方式来存取,是程序看起来更为直观,更容易编写. 1.索引器的定义 C#中的类成员可以是任意类型,包括数组和集合.当一个类包含了数组和集合成员时,索引器 ...

  6. Asp.Net中索引器的用法

    索引器定义类似于属性,但其功能与属性并不相同.索引器提供一种特殊的方法编写get和set访问器.属性可以像访问字段一样访问对象的数据,索引器可以使用户像访问数组一样访问类成员. 一.索引器特性 1.g ...

  7. C#中索引器Indexer的学习使用

    索引器 顾名思义,是用来索引的,那么C#中索引器是用来索引什么的呢 首先我们知道,C#中的数组是本身就可以索引的,那么C#中的类和结构呢,类和结构的实例是无法索引的,如果我们想让C#中类或者结构的实例 ...

  8. C#中如何应用索引器 ( How to use Indexers )

    C#中索引器是个好东西, 可以允许类或者结构的实例像数组一样进行索引. 在foreach或者直接索引时很有用. 使用索引器可以简化客户端代码, 即调用者可以简化语法,直观理解类及其用途. 索引器只能根 ...

  9. 【c# 学习笔记】索引器

    当一个类包含数组成员时,索引器 的使用将大大地简化对类中数组成员的访问.索引器的定义类似于属性,也具有GET访问器和set访问器,如下: [修饰符] 数据类型 this[索引类型 index] { g ...

随机推荐

  1. k8s系列---存储卷pv/pvc。configMap/secert

    因为pod是有生命周期的,pod一重启,里面的数据就没了.所以我们需要数据持久化存储. 在k8s中,存储卷不属于容器,而是属于pod.也就是说同一个pod中的容器可以共享一个存储卷. 存储卷可以是宿主 ...

  2. centos6.x下yum安装heartbeat

    [root@heartbeat ~]# uname -n #<===配置heartbeat时,节点的主机名必须和 uname -n 命令的结果要一致 heartbeat [root@heartb ...

  3. centos7使用MySQL的Yum存储库安装mysql5.7.27

    下载yum源 官网地址:http://dev.mysql.com/downloads/repo/yum/ centos7系统: http://dev.mysql.com/get/mysql57-com ...

  4. redis系列-要命的zrangebyscore

    0x0 引子 无论做哪种业务都躲不开排行功能.Redis 的 Sorted Sets 结构就是为排行而生的.它简单易用,效率奇高.同时它也有坑,你真的了解它吗? 老规矩,先讲故事,后科普.这里是 So ...

  5. Linux安装Redis,在测试阶段即make test出现“You need tcl 8.5 or newer in order to run the Redis test”问题解决方案

    Linux安装Redis,在测试阶段即make test出现"You need tcl 8.5 or newer in order to run the Redis test"问题 ...

  6. python网络爬虫(三)requests库的13个控制访问参数及简单案例

    酱酱~小编又来啦~

  7. 《果壳中的C# C# 5.0 权威指南》 - 学习笔记

    <果壳中的C# C# 5.0 权威指南> ========== ========== ==========[作者] (美) Joseph Albahari (美) Ben Albahari ...

  8. robotframework安装与详解

    Robot Framework(以下简称rf)是一款python编写的功能自动化测试框架.具备良好的可扩展性,支持关键字驱动,可以同时测试多种类型的客户端或者接口,可以进行分布式测试执行.主要用于轮次 ...

  9. clr via c# 接口

    1,常用接口及其定义 public interface IDisposable{ void Dispose(); } public interface IEnumerable}{ IEnumerato ...

  10. 《自拍教程21》mediainfo_多媒体文件查看工具

    mediainfo命令介绍 mediainfo.exe(Linux/iMac下是未带后缀的mediainfo), 是一款音视频图片文件的信息查询工具, 常用于查看多媒体文件的视频流信息,音频流信息,字 ...