• 学习c#的第二十二天


    目录

    C# 索引器(Indexer)

    表达式主体定义

    索引器概述

    使用索引器

    备注

    示例 1

    可靠编程

    接口中的索引器

    属性和索引器之间的比较


    C# 索引器(Indexer)

    索引器是C#编程语言中的一个特性,它允许类或结构的实例像数组一样进行索引操作,而无需显式指定类型或实例成员。通过索引器,可以设置或检索特定索引位置的值。索引器类似于属性,但不同之处在于索引器的访问器需要使用参数。

    下面是一个简单的示例,定义了一个泛型类,其中包含用于赋值和检索值的简单getset访问器方法:

    1. public class SampleCollection<T>
    2. {
    3. private T[] arr = new T[100];
    4. public T this[int i]
    5. {
    6. get { return arr[i]; }
    7. set { arr[i] = value; }
    8. }
    9. }

    在上面的示例中,定义了一个名为SampleCollection的泛型类,该类包含一个私有的T类型数组arr,并且定义了一个索引器this,用于对arr数组进行索引操作。

    下面是一个使用索引器的示例程序:

    1. class Program
    2. {
    3. static void Main()
    4. {
    5. var stringCollection = new SampleCollection<string>();
    6. stringCollection[0] = "Hello, ";
    7. stringCollection[1] = "world!";
    8. Console.Write(stringCollection[0] + stringCollection[1]);
    9. }
    10. }

    在上面的示例程序中,创建了SampleCollection类的一个实例stringCollection,并使用索引器对其进行赋值和检索操作。最终输出"Hello, world!"。

    表达式主体定义

    当索引器的 get 或 set 访问器包含一个简单的返回或设置值的语句时,可以使用表达式主体定义来简化代码。自C# 6起,表达式主体成员提供了一种经过简化的语法,使得只读索引器的实现变得更加简洁。以下是一个示例:

    1. public class SampleCollection<T>
    2. {
    3. private T[] arr = new T[100];
    4. public T this[int i] => arr[i];
    5. }

    在上面的示例中,我们定义了一个只读的索引器,使用了表达式主体定义。请注意,=> 引入了表达式主体,并未使用 get 关键字。这等价于下面的完整定义:

    1. public class SampleCollection<T>
    2. {
    3. private T[] arr = new T[100];
    4. public T this[int i]
    5. {
    6. get { return arr[i]; }
    7. }
    8. }

    通过使用表达式主体定义,可以将简单的只读索引器的实现代码变得更加简洁和易读。

    自 C# 7.0 起,我们可以使用表达式主体定义来实现索引器的 get 和 set 访问器。对于只读索引器,我们可以使用简化的语法,不需要显式地使用 get 关键字。而对于读写索引器,需要同时使用 get 和 set 关键字。

    下面是一个示例,展示了如何在 C# 7.0 及更新版本中使用表达式主体定义来实现只读和读写索引器:

    1. public class SampleCollection<T>
    2. {
    3. private T[] arr = new T[100];
    4. // 只读索引器的表达式主体定义
    5. public T this[int i] => arr[i];
    6. // 读写索引器的表达式主体定义
    7. public T this[int i]
    8. {
    9. get => arr[i];
    10. set => arr[i] = value;
    11. }
    12. }

    在这个示例中,我们展示了使用表达式主体定义来实现只读和读写索引器。对于只读索引器,我们可以省略显式的 get 关键字;而对于读写索引器,需要同时使用 get 和 set 关键字。

    索引器概述

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

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

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

    • value 关键字用于定义由 set 访问器分配的值。

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

    • 索引器可被重载。

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

    使用索引器

    索引器使你可从语法上方便地创建结构接口,以便客户端应用程序可以像访问数组一样访问它们。 编译器将生成一个 Item 属性(或者如果存在 IndexerNameAttribute,也可以生成一个命名属性)和适当的访问器方法。 在主要目标是封装内部集合或数组的类型中,常常要实现索引器。 例如,假设有一个类 TempRecord,它表示 24 小时的周期内在 10 个不同时间点所记录的温度(单位为华氏度)。 此类包含一个 float[] 类型的数组 temps,用于存储温度值。 通过在此类中实现索引器,客户端可采用 float temp = tempRecord[4] 的形式(而非 float temp = tempRecord.temps[4])访问 TempRecord 实例中的温度。 索引器表示法不但简化了客户端应用程序的语法;还使类及其目标更容易直观地为其它开发者所理解。

    若要在类或结构上声明索引器,请使用 this 关键字,如以下示例所示:

    1. // 索引器声明
    2. public int this[int index]
    3. {
    4. // 获取和设置访问器
    5. }

    注意:通过声明索引器,可自动在对象上生成一个名为 Item 的属性。 无法从实例成员访问表达式直接访问 Item 属性。 此外,如果通过索引器向对象添加自己的 Item 属性,则将收到 CS0102 编译器错误。 要避免此错误,请使用 IndexerNameAttribute 来重命名索引器,详细信息如下所示。

    备注

    索引器及其参数的类型必须至少具有和索引器相同的可访问性。 有关可访问性级别的详细信息,请参阅访问修饰符

    有关如何在接口上使用索引器的详细信息,请参阅接口索引器

    索引器的签名由其形参的数目和类型所组成。 它不包含索引器类型或形参的名称。 如果要在相同类中声明多个索引器,则它们的签名必须不同。

    索引器未分类为变量;因此,索引器值不能按引用(作为 ref 或 out 参数)传递,除非其值是引用(即按引用返回。)

    若要使索引器的名称可为其他语言所用,请使用 System.Runtime.CompilerServices.IndexerNameAttribute,如以下示例所示:

    1. // Indexer declaration
    2. [System.Runtime.CompilerServices.IndexerName("TheItem")]
    3. public int this[int index]
    4. {
    5. // get and set accessors
    6. }

    此索引器被索引器名称属性重写,因此其名称将为 TheItem。 默认情况下,默认名称为 Item。

    示例 1

    1. using System;
    2. public class SampleCollection<T>
    3. {
    4. private T[] arr = new T[100];
    5. // 定义索引器
    6. public T this[int i]
    7. {
    8. get { return arr[i]; }
    9. set { arr[i] = value; }
    10. }
    11. }
    12. public class Program
    13. {
    14. public static void Main()
    15. {
    16. // 创建SampleCollection实例
    17. SampleCollection<string> collection = new SampleCollection<string>();
    18. // 设置索引器的值
    19. collection[0] = "Hello";
    20. collection[1] = "World";
    21. // 获取索引器的值
    22. string firstElement = collection[0];
    23. string secondElement = collection[1];
    24. // 输出结果
    25. Console.WriteLine(firstElement); // 输出: Hello
    26. Console.WriteLine(secondElement); // 输出: World
    27. }
    28. }

    在上述示例中,我们定义了一个名为 SampleCollection 的泛型类,它具有一个内部数组 arr,长度为100。然后,我们通过定义索引器使得可以像访问数组一样访问 SampleCollection 实例的元素。

    在 Main 方法中,我们创建了 SampleCollection 类的实例 collection。通过设置索引器的值,我们将字符串 "Hello" 和 "World" 分别赋值给索引 0 和索引 1 的元素。然后,我们通过获取索引器的值,将其赋值给 firstElement 和 secondElement 变量。

    最后,我们将 firstElement 和 secondElement 的值输出到控制台,验证索引器的使用结果。

    可靠编程

    提高索引器的安全性和可靠性有两种主要方法:

    • 请确保结合某一类型的错误处理策略,以处理万一客户端代码传入无效索引值的情况。 在本主题前面的第一个示例中,TempRecord 类提供了 Length 属性,使客户端代码能在将输入传递给索引器之前对其进行验证。 也可将错误处理代码放入索引器自身内部。 请确保为用户记录在索引器的访问器中引发的任何异常。

    • 在可接受的程度内,为 get 和 set 访问器的可访问性设置尽可能多的限制。 这一点对  set 访问器尤为重要。 有关详细信息,请参阅限制访问器可访问性

    接口中的索引器

    在C#中,接口是抽象类的一种形式,它定义了一个类或结构体应遵循的成员列表。然而,在接口中并不能直接包含字段,因此无法在接口中定义索引器。索引器必须与具体的实现相关联,而接口只能定义方法、属性、事件和索引器的成员契约,而不包含实际的实现。

    换句话说,接口可以定义一个索引器的契约,但具体的实现需要在实现接口的类中完成。下面是一个示例:

    1. using System;
    2. public interface IIndexable
    3. {
    4. // 索引器的契约
    5. string this[int index] { get; set; }
    6. }
    7. public class MyCollection : IIndexable
    8. {
    9. private string[] _data = new string[100];
    10. // 实现接口中定义的索引器
    11. public string this[int index]
    12. {
    13. get { return _data[index]; }
    14. set { _data[index] = value; }
    15. }
    16. }
    17. public class Program
    18. {
    19. public static void Main()
    20. {
    21. MyCollection collection = new MyCollection();
    22. collection[0] = "Hello";
    23. collection[1] = "World";
    24. Console.WriteLine(collection[0]); // 输出: Hello
    25. Console.WriteLine(collection[1]); // 输出: World
    26. }
    27. }

    在上述示例中,我们定义了一个名为 IIndexable 的接口,其中包含了一个索引器的契约。然后,在 MyCollection 类中,我们实现了 IIndexable 接口,并提供了对应的索引器实现。

    通过这种方式,我们可以在接口中定义索引器的契约,然后在实现接口的类中完成对索引器的具体实现。这样做的好处是,当多个类需要实现相同的索引器行为时,可以通过接口来统一规范,从而提高代码的可维护性和扩展性。

    属性和索引器之间的比较

    索引器与属性在许多方面相似,但确实存在一些重要的差异。以下是这两者之间的主要区别:

    1. 访问方式:属性通过使用简单的名称来访问,而索引器使用索引来访问。属性可以像访问字段一样进行访问,而索引器使用类似于数组的语法来访问集合中的元素。

    2. 成员类型:属性可以是静态成员或实例成员,而索引器只能是实例成员。这意味着索引器只能通过实例对象进行访问。

    3. 参数:属性的get访问器没有参数,而索引器的get访问器具有与索引器相同的形参列表,以便接受索引参数。同样,属性的 set 访问器具有一个隐式的 value 参数,而索引器的 set 访问器也具有与索引器相同的形参列表,包括 value 参数。

    4. 简洁语法:对于属性,可以使用 自动实现的属性 来提供简洁的语法,而索引器只支持expression-bodied成员的简洁语法。

    综上所述,尽管属性和索引器有许多相似之处,但它们在访问方式、成员类型、参数和语法等方面存在一些重要的差异。

  • 相关阅读:
    Android C++系列:Linux进程间关系
    php 反射
    Qt QImag图像保存、格式转换
    uvm中的config机制方法总结(二)
    Flutter NestedScrollView 、SliverAppBar全解析,悬浮菜单的应用
    【愚公系列】2022年07月 Go教学课程 023-Go容器之列表
    C语言文件操作(四) —— 文件读取结束的判定(feof、ferror)
    钉钉与实在智能达成战略合作,实在Agent助力钉钉AI助理成为“新质生产力”
    提升Mac运行速度的三大方法
    Linux系统编程-进程信号(万字总结,用一周时间,很详细,建议收藏)
  • 原文地址:https://blog.csdn.net/m0_74293254/article/details/134504670