学习c#的第二十二天

2023-12-13 13:22:30

目录

C#?索引器(Indexer)

表达式主体定义

索引器概述

使用索引器

备注

示例 1

可靠编程

接口中的索引器

属性和索引器之间的比较


C#?索引器(Indexer)

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

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

public class SampleCollection<T>
{
    private T[] arr = new T[100];

    public T this[int i]
    {
        get { return arr[i]; }
        set { arr[i] = value; }
    }
}

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

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

class Program
{
    static void Main()
    {
        var stringCollection = new SampleCollection<string>();
        stringCollection[0] = "Hello, ";
        stringCollection[1] = "world!";

        Console.Write(stringCollection[0] + stringCollection[1]);
    }
}

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

表达式主体定义

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

public class SampleCollection<T>
{
    private T[] arr = new T[100];

    public T this[int i] => arr[i];
}

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

public class SampleCollection<T>
{
    private T[] arr = new T[100];

    public T this[int i]
    {
        get { return arr[i]; }
    }
}

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

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

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

public class SampleCollection<T>
{
    private T[] arr = new T[100];

    // 只读索引器的表达式主体定义
    public T this[int i] => arr[i];

    // 读写索引器的表达式主体定义
    public T this[int i]
    {
        get => arr[i];
        set => arr[i] = value;
    }
}

在这个示例中,我们展示了使用表达式主体定义来实现只读和读写索引器。对于只读索引器,我们可以省略显式的 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?关键字,如以下示例所示:

// 索引器声明
public int this[int index]
{
    // 获取和设置访问器
}

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

备注

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

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

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

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

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

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

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

示例 1

using System;

public class SampleCollection<T>
{
    private T[] arr = new T[100];

    // 定义索引器
    public T this[int i]
    {
        get { return arr[i]; }
        set { arr[i] = value; }
    }
}

public class Program
{
    public static void Main()
    {
        // 创建SampleCollection实例
        SampleCollection<string> collection = new SampleCollection<string>();

        // 设置索引器的值
        collection[0] = "Hello";
        collection[1] = "World";

        // 获取索引器的值
        string firstElement = collection[0];
        string secondElement = collection[1];

        // 输出结果
        Console.WriteLine(firstElement);   // 输出: Hello
        Console.WriteLine(secondElement);  // 输出: World
    }
}

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

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

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

可靠编程

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

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

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

接口中的索引器

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

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

using System;

public interface IIndexable
{
    // 索引器的契约
    string this[int index] { get; set; }
}

public class MyCollection : IIndexable
{
    private string[] _data = new string[100];

    // 实现接口中定义的索引器
    public string this[int index]
    {
        get { return _data[index]; }
        set { _data[index] = value; }
    }
}

public class Program
{
    public static void Main()
    {
        MyCollection collection = new MyCollection();

        collection[0] = "Hello";
        collection[1] = "World";

        Console.WriteLine(collection[0]);  // 输出: Hello
        Console.WriteLine(collection[1]);  // 输出: World
    }
}

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

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

属性和索引器之间的比较

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

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

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

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

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

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

文章来源:https://blog.csdn.net/m0_74293254/article/details/134504670
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。