概述

此部分内容引用自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. Tomcat 配置2 tomcat-users.xml

    Tomcat的配置 Tomcat的主要配置文件有3个,分别是:    Tomcat-users.xml.    web.xml    server.xml. 配置Tomcat-users.xml 该文 ...

  2. 浅析Internet上使用的安全协议

    Internet上使用的安全协议 网络安全是分层实现的,从应用层安全到数据链路层安全. 一.运输层安全协议:安全套接字SSL 1.1.简介 SSL 是安全套接层 (Secure Socket Laye ...

  3. input event兼容性

    <div class="wrapper"> <p>keypress - event not call on adroid</p> <inp ...

  4. codewars--js--Write Number in Expanded Form—filters、map、reduce、forEach

    问题描述: you will be given a number and you will need to return it as a string in Expanded Form. For ex ...

  5. SpringCloud之Ribbon负载均衡的入门操作

    使用Ribbon进行负载均衡 在使用Ribbon之前,我们先想一个之前的问题,之前我们将服务提供者注册进了eureka注册中心,但是在消费者端,我们还是使用的restTemplate调用的时候,其中写 ...

  6. Cesium案例解析(四)——3DModels模型加载

    目录 1. 概述 2. 代码 3. 解析 4. 参考 1. 概述 Cesium自带的3D Models示例,展示了如何加载glTF格式三维模型数据.glTF是为WebGL量身定制的数据格式,在网络环境 ...

  7. 泡泡后台Couchbase缓存使用经验分享

    一.导读 爱奇艺的社交业务“泡泡”,拥有日活用户6千万+,后台系统每日高峰期间接口QPS可以达到80K+,与视频业务的主要区别是泡泡业务更多地引入了与用户互动相关的数据,读.写的量均很大.无论是庞大的 ...

  8. Python和Anoconda和Pycharm联合使用教程

    简介 Python是一种跨平台的计算机程序设计语言.是一种面向对象的动态类型语言,最初被设计用于编写自动化脚本(shell),随着版本的不断更新和语言新功能的添加,越多被用于独立的.大型项目的开发. ...

  9. springboot快速创建项目框架

    一.项目框架准备 1.1 新建maven空项目,并在pom中引入依赖 <parent> <groupId>org.springframework.boot</groupI ...

  10. MySQL基础(3) | 函数

    MySQL基础(3) | 函数 前言 MySQL只有标量值函数的概念,没有SqlServer那种表值函数. 语法 创建 create function f_add( a int, b int ) re ...