美文网首页
c#学习 每日问题汇总 2024-03-11

c#学习 每日问题汇总 2024-03-11

作者: guocx_ | 来源:发表于2024-03-11 11:40 被阅读0次

1.索引器的使用以及理解索引器的重载,索引器的链式调用

在C#中,索引器(Indexer)是一种特殊的属性,它允许您使用数组的索引方式来访问类的实例。索引器使得类的对象可以像数组一样通过索引来访问其元素。这在处理集合或列表时特别有用,因为它提供了一种统一的方式来访问集合中的元素。

索引器的语法如下:

public class MyClass
{
    private int[] _items; // 假设我们有一个整数数组

    // 索引器的定义
    public int this[int index]
    {
        get
        {
            // 获取操作
            if (index < 0 || index >= _items.Length)
            {
                throw new IndexOutOfRangeException("索引超出范围。");
            }
            return _items[index];
        }
        set
        {
            // 设置操作
            if (index < 0 || index >= _items.Length)
            {
                throw new IndexOutOfRangeException("索引超出范围。");
            }
            _items[index] = value;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        MyClass myClass = new MyClass();
        myClass._items = new int[] { 1, 2, 3, 4, 5 };

        // 使用索引器访问元素
        Console.WriteLine(myClass[2]); // 输出: 3

        // 使用索引器设置元素值
        myClass[2] = 10;
        Console.WriteLine(myClass[2]); // 输出: 10
    }
}

在这个例子中,MyClass 有一个名为 _items 的私有整数数组。我们定义了一个名为 this 的索引器,它允许我们通过索引来访问和设置 _items 数组的元素。索引器的 get 访问器用于获取指定索引处的值,而 set 访问器用于设置该索引处的值。

用户可以通过 myClass[2] 这样的语法来访问或设置 _items 数组的第三个元素(索引从0开始)。这种方式与访问数组元素的方式非常相似,使得类的对象可以像数组一样被操作。

需要注意的是,索引器的名称 this 是特殊的,它表示索引器。在索引器的定义中,我们使用 int index 作为参数来表示索引,您可以根据需要使用不同的数据类型作为索引。此外,索引器可以有多个参数,以便实现多维索引。

如果您的属性是一个三维数组,您需要在索引器中定义三个维度的索引。以下是一个简单的例子,展示了如何为三维数组定义索引器:

public class MyClass
{
    private int[,,] _items; // 三维整数数组

    // 三维索引器的定义,使用index1, index2, index3作为参数名
    public int this[int index1, int index2, int index3]
    {
        get
        {
            // 获取操作
            if (index1 < 0 || index1 >= _items.GetLength(0) ||
                index2 < 0 || index2 >= _items.GetLength(1) ||
                index3 < 0 || index3 >= _items.GetLength(2))
            {
                throw new IndexOutOfRangeException("索引超出范围。");
            }
            return _items[index1, index2, index3];
        }
        set
        {
            // 设置操作
            if (index1 < 0 || index1 >= _items.GetLength(0) ||
                index2 < 0 || index2 >= _items.GetLength(1) ||
                index3 < 0 || index3 >= _items.GetLength(2))
            {
                throw new IndexOutOfRangeException("索引超出范围。");
            }
            _items[index1, index2, index3] = value;
        }
    }

    // 初始化三维数组的方法
    public void InitializeArray(int size1, int size2, int size3)
    {
        _items = new int[size1, size2, size3];
        // 初始化数组或执行其他操作
    }
}

class Program
{
    static void Main(string[] args)
    {
        MyClass myClass = new MyClass();
        myClass.InitializeArray(3, 3, 3); // 初始化一个3x3x3的三维数组

        // 使用三维索引器访问元素
        Console.WriteLine(myClass[1, 1, 1]); // 输出数组中的某个元素

        // 使用三维索引器设置元素值
        myClass[1, 1, 1] = 10;
        Console.WriteLine(myClass[1, 1, 1]); // 输出修改后的元素值
    }
}

在这个例子中,MyClass 有一个名为 _items 的三维整数数组。我们定义了一个三维索引器,它接受三个整数参数:index1index2index3。这些参数分别代表三维数组的三个维度。在 getset 访问器中,我们使用这些参数来访问或设置数组中的特定元素。

InitializeArray 方法用于初始化三维数组,您可以根据需要设置数组的大小。在 Main 方法中,我们创建了 MyClass 的实例,初始化了三维数组,并使用三维索引器来访问和设置数组中的元素。

请注意,三维数组的索引器需要检查每个维度的索引是否在数组的边界内。我们使用 GetLength 方法来获取每个维度的大小,并在索引超出范围时抛出 IndexOutOfRangeException 异常。

上面的代码定义了一个名为 MyClass 的类,它包含了一个三维整数数组作为其属性,并为这个数组提供了一个三维索引器。下面是对代码的详细解释:

  1. 三维数组的声明
private int[,,] _items; // 三维整数数组

这里声明了一个名为 _items 的三维整数数组。在C#中,三维数组使用2个逗号来分隔维度。

  1. 三维索引器的定义
public int this[int index1, int index2, int index3]
{
    // ...
}

索引器使用 this 关键字定义,它允许我们使用类似数组的语法来访问类的实例。在这个例子中,索引器接受三个参数:index1index2index3,分别对应三维数组的三个维度。

  1. 索引器的 get 访问器
get
{
    // ...
}

get 访问器用于从数组中检索值。首先,它检查提供的索引是否在数组的边界内。这是通过调用 GetLength 方法并传递相应的维度索引来完成的。如果索引有效,它返回数组中指定位置的值。如果索引无效,它抛出 IndexOutOfRangeException 异常。

  1. 索引器的 set 访问器
set
{
    // ...
}

set 访问器用于在数组的指定位置设置值。与 get 访问器类似,它也检查索引的有效性。如果索引有效,它将值赋给数组的指定位置。如果索引无效,同样抛出 IndexOutOfRangeException 异常。

  1. 初始化三维数组的方法
public void InitializeArray(int size1, int size2, int size3)
{
    _items = new int[size1, size2, size3];
    // 初始化数组或执行其他操作
}

这个方法用于初始化三维数组。它接受三个参数,分别代表数组在每个维度上的大小,并创建一个新的三维数组。您可以在这个方法中添加额外的逻辑来初始化数组的值或执行其他操作。

  1. Main 方法中使用索引器
MyClass myClass = new MyClass();
myClass.InitializeArray(3, 3, 3); // 初始化一个3x3x3的三维数组
// 使用三维索引器访问和设置元素

Main 方法中,我们创建了 MyClass 的一个实例,并调用 InitializeArray 方法来初始化三维数组。然后,我们使用三维索引器来访问和设置数组中的元素,就像操作普通数组一样。

这个例子展示了如何在C#中为三维数组创建索引器,并提供了一种直观的方式来访问和修改数组的元素。

定义了一个名为 Person 的类,其中包含了三个字符数组字段:_show_info_music。类中定义了两个索引器,一个是基本的一维索引器,另一个是重载后的索引器,它接受一个字符串参数 type 和一个整数索引 index

class Person
{
    // 字段:数组一
    private char[] _show = { '就', '是', '有', '这', '种', '操', '作', '!' };
    // 字段:数组二
    private char[] _info = { '我', '是', '练', '习', '时', '长', '两', '年', '半', '的', '偶', '像', '练', '习', '生' };
    // 字段:数组三
    private char[] _music = { '音', '乐', '响', '起', '!', '鸡', '你', '太', '美' };

    // 基本索引器
    public char this[int index]
    {
        get
        {
            if (index >= 0 && index < _show.Length)
            {
                return _show[index];
            }
            else
            {
                Console.WriteLine("取值的索引超出范围");
                return '\0';
            }
        }
        set
        {
            if (index >= 0 && index < _show.Length)
            {
                _show[index] = value;
            }
            else
            {
                Console.WriteLine("赋值的索引超出范围");
            }
        }
    }

    // 重载索引器
    public char this[string type, int index]
    {
        get
        {
            char result = '\0';
            switch (type)
            {
                case "数组一":
                    if (index >= 0 && index < _show.Length)
                    {
                        return _show[index];
                    }
                    break;
                case "数组二":
                    if (index >= 0 && index < _info.Length)
                    {
                        return _info[index];
                    }
                    break;
                case "数组三":
                    if (index >= 0 && index < _music.Length)
                    {
                        return _music[index];
                    }
                    break;
                default:
                    Console.WriteLine("不在数组范围!请输入:数组一/数组二/数组三");
                    break;
            }
            return result;
        }
        set
        {
            switch (type)
            {
                case "数组一":
                    if (index >= 0 && index < _show.Length)
                    {
                        _show[index] = value;
                    }
                    else
                    {
                        Console.WriteLine("赋值的索引超出范围");
                    }
                    break;
                // 为数组二和数组三添加类似的逻辑
                default:
                    Console.WriteLine("不在数组范围!请输入:数组一/数组二/数组三");
                    break;
            }
        }
    }
}
  1. 基本索引器
    这个索引器允许您通过整数索引来访问和设置 _show 数组中的字符。如果索引超出了 _show 数组的范围,它会提示用户索引超出范围,并返回一个空字符 '\0'

  2. 重载索引器
    这个索引器允许您通过指定的数组类型("数组一"、"数组二" 或 "数组三")和整数索引来访问和设置对应的数组。它使用 switch 语句来判断用户输入的 type,并根据 type 返回对应的数组中的字符。如果索引超出了指定数组的范围,它会提示用户重新输入。

以下是对重载索引器的一些改进和解释:

  • get访问器中,如果type不匹配任何已知的数组类型,应该返回一个默认值(例如'\0'),而不是result,因为result被初始化为'\0'
  • set访问器中,您可能也想添加类似的逻辑,以允许用户通过重载的索引器设置数组的值。
  • set访问器中,您应该在所有情况下返回void,因为您正在设置值而不是返回值。
  • 为重载索引器的 set 访问器添加了与 get 访问器相似的逻辑。这样,用户就可以通过指定数组类型和索引来设置数组中的值了。如果索引超出范围,它会提示用户索引超出范围。

索引器的重载:

public char this[string type, int index]
{
    get
    {
        // ...(索引器的代码)
    }
    set
    {
        // ...(索引器的代码)
    }
}

这段代码定义了一个名为 this 的索引器,它是 Person 类的一部分。这个索引器是重载的,意味着它有两个参数:一个字符串 type 和一个整数 index。索引器允许您使用类似于数组的语法来访问和设置类的内部数组的元素。

  • get 访问器:当您尝试从索引器获取值时(例如 person["数组一", 0]),get 访问器会被调用。它根据传入的 type 参数确定要访问哪个数组,然后使用 index 参数来获取数组中特定位置的元素。

  • set 访问器:当您尝试设置索引器的值时(例如 person["数组一", 0] = 'A'),set 访问器会被调用。它同样根据 type 参数确定要操作哪个数组,并将新的值设置到指定的 index 位置。

<u>这里是 getset 访问器的内部逻辑的简化版本:</u>

public char this[string type, int index]
{
    get
    {
        // 根据type参数选择正确的数组
        char[] selectedArray;
        switch (type)
        {
            case "数组一":
                selectedArray = _show;
                break;
            case "数组二":
                selectedArray = _info;
                break;
            case "数组三":
                selectedArray = _music;
                break;
            default:
                Console.WriteLine("无效的数组类型。");
                return '\0'; // 返回一个空字符
        }

        // 检查索引是否在数组的有效范围内
        if (index >= 0 && index < selectedArray.Length)
        {
            return selectedArray[index];
        }
        else
        {
            Console.WriteLine("索引超出范围。");
            return '\0'; // 返回一个空字符
        }
    }

    set
    {
        // 根据type参数选择正确的数组
        char[] selectedArray;
        switch (type)
        {
            case "数组一":
                selectedArray = _show;
                break;
            case "数组二":
                selectedArray = _info;
                break;
            case "数组三":
                selectedArray = _music;
                break;
            default:
                Console.WriteLine("无效的数组类型。");
                return;
        }

        // 检查索引是否在数组的有效范围内
        if (index >= 0 && index < selectedArray.Length)
        {
            selectedArray[index] = value;
        }
        else
        {
            Console.WriteLine("索引超出范围。");
        }
    }
}

在这个简化的例子中,getset 访问器都使用 switch 语句来确定要操作的数组。然后,它们检查索引是否在数组的有效范围内。如果是,get 访问器返回数组中指定索引的元素,而 set 访问器将新值设置到指定索引。如果索引无效,它们会在控制台输出错误消息。

Main 方法中,您可以通过传递数组类型和索引来调用这个索引器,例如:

Person person = new Person();
Console.WriteLine(person["数组一", 0]); // 输出数组一的第一个元素
person["数组一", 0] = 'A'; // 将数组一的第一个元素设置为 'A'

这样,您就可以通过索引器来访问和修改 Person 类中定义的数组了。

<u>public char this[string type, int index]</u> 这部分代码定义了一个索引器,它不是固定必须这样写,但它遵循了C#中索引器的语法规则。索引器的语法允许您定义一个类似于数组的索引访问模式,但您可以根据需要自定义参数和返回类型。

在这个特定的例子中:

  • public是访问修饰符,表示这个索引器可以在类的外部被访问。
  • char是返回类型,表示这个索引器返回一个字符(char类型)。
  • this是索引器的关键字,它在这里用作方法名,表示这是一个索引器。
  • [string type, int index]是索引器的参数列表,其中type是一个字符串参数,用于指定要访问的数组类型;index是一个整数参数,用于指定数组中的索引位置。

您可以选择不同的参数类型和数量来满足您的具体需求。例如,如果您的类中只有一个数组,或者您希望索引器只接受一个参数,您可以简化索引器的定义。但在这个例子中,由于有三个不同的数组,所以需要一个额外的参数来区分它们。

如果您不希望使用字符串来指定数组类型,您也可以定义三个不同的索引器,每个索引器对应一个数组,如下所示:

public char this[int index] // 用于数组一
{
    get { /* ... */ }
    set { /* ... */ }
}

public char this[string type, int index] // 用于数组二和数组三
{
    get { /* ... */ }
    set { /* ... */ }
}

在这个例子中,我们为每个数组定义了一个单独的索引器。这样,您可以根据数组的类型直接调用相应的索引器,而不需要传递额外的类型参数。

总之,索引器的写法不是固定的,您可以根据实际情况和需求来设计索引器的签名。

下面是一个例子,展示了如何为一个包含多个不同类型属性的类定义两个不同类型的索引器重载。假设我们有一个名为 Bookshelf 的类,它用来表示书架,书架上可以放不同类型的书籍(例如小说、教科书和杂志)。我们想要通过索引器来访问特定类型的书籍。

using System;

public class Bookshelf
{
    // 字段:存储不同类型的书籍
    private List<Book> novels;
    private List<教科书> textbooks;
    private List<Magazine> magazines;

    // 构造函数,初始化书籍列表
    public Bookshelf()
    {
        novels = new List<Book>();
        textbooks = new List<教科书>();
        magazines = new List<Magazine>();
    }

    // 索引器重载1:用于访问小说
    public Book this[int index, BookType type]
    {
        get
        {
            if (type == BookType.Novel)
            {
                if (index >= 0 && index < novels.Count)
                {
                    return novels[index];
                }
            }
            else
            {
                throw new ArgumentException("Invalid book type for this indexer.");
            }
        }
        set
        {
            if (type == BookType.Novel)
            {
                if (index >= 0 && index < novels.Count)
                {
                    novels[index] = value;
                }
                else
                {
                    throw new IndexOutOfRangeException("Index out of range.");
                }
            }
            else
            {
                throw new ArgumentException("Invalid book type for this indexer.");
            }
        }
    }

    // 索引器重载2:用于访问教科书和杂志
    public Book this[int index, BookType type]
    {
        get
        {
            switch (type)
            {
                case BookType.Textbook:
                    if (index >= 0 && index < textbooks.Count)
                    {
                        return textbooks[index];
                    }
                    break;
                case BookType.Magazine:
                    if (index >= 0 && index < magazines.Count)
                    {
                        return magazines[index];
                    }
                    break;
                default:
                    throw new ArgumentException("Invalid book type for this indexer.");
            }
        }
        set
        {
            switch (type)
            {
                case BookType.Textbook:
                    if (index >= 0 && index < textbooks.Count)
                    {
                        textbooks[index] = (教科书) value;
                    }
                    else
                    {
                        throw new IndexOutOfRangeException("Index out of range.");
                    }
                    break;
                case BookType.Magazine:
                    if (index >= 0 && index < magazines.Count)
                    {
                        magazines[index] = (Magazine) value;
                    }
                    else
                    {
                        throw new IndexOutOfRangeException("Index out of range.");
                    }
                    break;
                default:
                    throw new ArgumentException("Invalid book type for this indexer.");
            }
        }
    }
}

// 假设这些是书籍的不同类型
public class Book { }
public class 教科书 : Book { }
public class Magazine : Book { }

// 枚举,表示书籍的类型
public enum BookType
{
    Novel,
    Tutorial,
    Magazine
}

class Program
{
    static void Main(string[] args)
    {
        Bookshelf shelf = new Bookshelf();

        // 添加一些书籍到书架
        shelf.novels.Add(new Book());
        shelf.textbooks.Add(new 教科书());
        shelf.magazines.Add(new Magazine());

        // 使用索引器访问和设置小说
        Book novel = shelf[0, BookType.Novel];
        shelf[0, BookType.Novel] = new Book();

        // 使用索引器访问和设置教科书
        教科书 textbook = shelf[0, BookType.Tutorial];
        shelf[0, BookType.Tutorial] = new 教科书();

        // 使用索引器访问和设置杂志
        Magazine magazine = shelf[0, BookType.Magazine];
        shelf[0, BookType.Magazine] = new Magazine();
    }
}

在这个例子中,我们定义了两个索引器重载:

  1. 第一个索引器this[int index, BookType type]专门用于访问和设置小说(Book类型)。
  2. 第二个索引器this[int index, BookType type]用于访问和设置教科书(教科书类型)和杂志(Magazine类型)。这里我们使用了switch语句来根据书籍类型选择正确的列表。

我们还定义了一个 BookType 枚举,用于表示书籍的类型。这样,当我们使用索引器时,可以通过传递 BookType 枚举值来指定要访问的书籍类型。

请注意,这个例子中的索引器重载可能会导致一些混淆,因为它们有相同的签名。在实际应用中,为了避免这种情况,您可能需要为每个索引器重载定义不同的参数列表。例如,您可以为每种书籍类型定义一个单独的索引器,或者使用不同的方法名。

索引器(Indexer)在C#中通常用于以下几种场景:

  1. 模拟数组或集合的访问
    当您想要提供一个类,使其行为类似于数组或集合,允许用户通过索引来访问或修改元素时,索引器非常有用。例如,自定义集合类、列表或字典。

  2. 封装数据结构
    如果您的类封装了一个复杂的数据结构(如二维数组、链表、树等),索引器可以提供一个简单的接口来访问这些数据结构中的元素。

  3. 数据库操作
    在与数据库交互时,索引器可以用来模拟数据库表的行和列。例如,您可以创建一个类来表示数据库中的表,索引器可以用于访问特定行和列的数据。

  4. 文件系统操作
    在处理文件系统时,索引器可以用来访问文件或目录列表。例如,一个类可以表示一个目录,索引器可以用于获取目录中的文件或子目录。

  5. 游戏开发
    在游戏开发中,索引器可以用来访问游戏对象的集合,如角色、敌人、子弹等。这使得代码更易于理解和维护。

  6. 数据绑定
    在用户界面编程中,索引器常用于实现数据绑定。例如,当您需要将一个列表绑定到用户界面的某个部分时,索引器可以简化数据的访问。

  7. 简化API
    索引器可以提供一个简洁的API,使得外部代码可以通过索引来操作类的状态,而不需要暴露内部的实现细节。

  8. 多维数据结构
    对于多维数据结构,如三维数组或自定义的多维集合,索引器可以提供一种直观的方式来访问特定维度的元素。

使用索引器时,您应该考虑其性能影响。索引器在每次访问时都会执行额外的代码,这可能会影响性能。因此,在性能敏感的应用中,您可能需要权衡索引器的便利性和性能开销。

索引器通常在以下情况下需要重载:

  1. 支持多种数据类型
    当您需要一个索引器来处理不同类型的数据时,您可能需要重载索引器。例如,如果您的类管理了多种类型的对象(如字符串、整数、自定义对象等),您可能需要为每种类型提供一个专门的索引器。

  2. 不同的访问模式
    如果您的类需要支持不同的访问模式,例如,一个索引器用于只读访问,另一个用于读写访问,您可能需要重载索引器以提供不同的行为。

  3. 不同的参数数量或类型
    当您需要根据不同的参数数量或类型来访问数据时,索引器重载是必要的。例如,一个索引器可能接受一个整数索引来访问一维数组,而另一个可能接受两个整数索引来访问二维数组。

  4. 不同的数据结构
    如果您的类封装了不同的数据结构(如列表、字典、集合等),您可能需要为每种数据结构提供一个专门的索引器。

  5. 提供更灵活的接口
    为了提供更灵活的接口,您可能需要重载索引器以支持不同的参数组合。例如,一个索引器可能接受一个字符串和一个整数来访问字典中的键值对,而另一个可能只接受一个字符串来访问特定的属性。

  6. 遵循特定的设计模式
    在某些设计模式中,如外观模式(Facade Pattern),您可能需要重载索引器来提供一个统一的接口来访问底层系统的多个组件。

  7. 满足特定的业务逻辑
    如果业务逻辑要求根据不同的条件来返回不同的数据,您可能需要重载索引器来实现这些条件逻辑。

在设计索引器重载时,您应该确保重载的索引器之间有明确的区分,以避免混淆。同时,您应该考虑索引器的使用场景和性能影响,确保索引器的使用不会对性能产生负面影响。如果可能,最好通过方法重载或属性来代替索引器重载,以保持代码的清晰和简洁。

索引器的链式调用:

namespace 索引器的理解
{
    class Student
    {
        //我是三好学生
        private char[] _info = null;

        public Student(char[] info)
        {
            this._info = info;
        }

        //构建索引器
        public char this[int index]
        {
            get
            {
                if (_info != null)
                {
                    if (index >= 0 && index <= _info.Length)
                    {
                        return this._info[index];
                    }
                    else
                    {
                        //提示不在数组的范围
                        Console.WriteLine("不在数组的索引范围[{0},{1}],请重新输入!", 0, _info.Length);
                        return '\0';
                    }
                }
                else
                {
                    return '\0';
                }
            }
            set
            {
                if (_info != null)
                {
                    if (index >= 0 && index <= _info.Length)
                    {
                        _info[index] = value;
                    }
                    else
                    {
                        //提示不在范围
                        Console.WriteLine("不在数组的索引范围[{0},{1}],请重新输入!", 0, _info.Length);
                    }
                }
                else
                {
                    //提示不在范围
                    Console.WriteLine("不在数组的索引范围[{0},{1}],请重新输入!", 0, _info.Length);
                }
            }
        }

    }
    class School
    {
        //字段:数组四
        private Student[] _student =
       {
        new Student(new char[] { '蔡', '徐', '坤' }),
        new Student(new char[] { '洪', '世', '贤' }),
        new Student(new char[] { '断', '水', '流','大','师','兄' }),
    };

        public Student this[int index]
        {
            get
            {
                if (index >= 0 && index < _student.Length)
                {
                    return _student[index];
                }
                else
                {
                    //提示不在数组的索引范围请重新输入
                    Console.WriteLine("不在数组的索引范围[{0},{1}],请重新输入!", 0, _student.Length);
                    return null;
                }
            }
            set
            {
                if (index >= 0 && index < _student.Length)
                {
                    _student[index] = value;
                }
                else
                {
                    //提示不在数组的索引范围请重新输入
                    Console.WriteLine("不在数组的索引范围[{0},{1}],请重新输入!", 0, _student.Length);
                }
            }
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            //打印对象数组当中的内容
            School school = new School();
            Console.Write(school[0][0]);
            Console.Write(school[0][1]);
            Console.Write(school[0][2]);

            Console.WriteLine();
            Console.Write(school[1][0]);
            Console.Write(school[1][1]);
            Console.Write(school[1][2]);

            Console.WriteLine();
            Console.Write(school[2][0]);
            Console.Write(school[2][1]);
            Console.Write(school[2][2]);
            Console.Write(school[2][3]);
            Console.Write(school[2][4]);
            Console.Write(school[2][5]);

            Console.ReadKey();
        }
    }


}

这段代码定义了两个类:StudentSchool,以及一个 Program 类,它包含了程序的入口点 Main 方法。下面是对这段代码的详细解释:

  1. Student 类
  • _info字段是一个字符数组,用于存储学生的姓名信息。
  • Student类的构造函数接受一个字符数组作为参数,并将其赋值给_info字段。
  • 类还定义了一个索引器,允许通过索引访问和设置_info字段中的单个字符。索引器的getset访问器都包含了边界检查,以确保索引值在有效范围内。如果索引超出范围,会在控制台输出错误消息,并返回一个空字符'\0'(在get访问器中)或不执行任何操作(在set访问器中)。
  1. School 类
  • _student字段是一个Student对象数组,每个对象代表一个学生。
  • 类同样定义了一个索引器,允许通过索引访问和设置_student数组中的Student对象。这个索引器也包含了边界检查,如果索引超出范围,会在控制台输出错误消息,并返回null(在get访问器中)或不执行任何操作(在set访问器中)。
  1. Program 类
  • Main方法是程序的入口点。
  • 创建了一个School类的实例school
  • 使用索引器访问school数组中的每个学生,并打印出学生的姓名。由于Student类的索引器允许访问_info字段中的字符,这里实际上是在打印每个学生的姓名的每个字符。

Main 方法中,程序尝试打印出每个学生的姓名。例如,school[0][0] 会打印出第一个学生姓名的第一个字符('蔡'),school[0][1] 会打印出第二个字符('徐'),依此类推。对于第三个学生,由于其姓名有六个字符,所以 school[2][5] 会打印出第六个字符('兄')。

这段代码演示了如何在类中使用索引器来模拟数组的索引访问,并提供了一种方式来处理索引超出范围的情况。同时,它也展示了如何在类的实例之间共享数据(在这个例子中是学生姓名)并对其进行操作。
打印学生名字的代码

School school = new School();
Console.Write(school[0][0]);
Console.Write(school[0][1]);
// ...

这段代码创建了一个 School 对象 school,然后通过索引器访问每个学生的名字的每个字符,并将其打印到控制台。例如,school[0][0] 访问第一个学生的名字的第一个字符。

school[0][0] 这样的表达式能够正常工作,是因为在 School 类和 Student 类中都定义了索引器。这里的索引器允许您通过方括号 [] 的语法来访问对象的属性或字段。这种语法通常用于数组或集合类型,但也可以用于任何定义了索引器的类。

让我们逐步分解 school[0][0] 这个表达式:

  1. schoolSchool类的一个实例。
  2. school[0]是对School类索引器的调用,它使用索引0来获取_student数组中的第一个Student对象。这个索引器返回的是Student类型的对象。
  3. school[0][0]中的第二个索引器调用实际上是对Student类索引器的调用。由于Student类的索引器接受一个整数索引,它尝试获取Student对象的_info字段中的第一个字符(索引为0)。

这种嵌套的索引器调用是可能的,因为 School 类的索引器返回了一个 Student 对象,而 Student 类也定义了自己的索引器。当您对 School 类的索引器返回的对象进行索引时,实际上是在调用那个对象的索引器。

这里是一个简化的例子来说明这个过程:

// School 类的索引器
public Student this[int index]
{
    get { return _student[index]; }
}

// Student 类的索引器
public char this[int index]
{
    get { return _info[index]; }
}

当您执行 school[0][0] 时:

  1. school[0]返回_student数组的第一个Student对象。
  2. school[0][0]实际上是(school[0])[0],即对第一个Student对象调用索引器,获取其_info字段的第一个字符。

这种索引器的链式调用在C#中是允许的,只要每个类都正确地定义了索引器,并且返回的类型支持进一步的索引访问。

让我们通过一个简单的例子来理解索引器的链式调用。假设我们有一个表示二维网格的类,这个类有两个索引器:一个用于访问网格的行(二维数组的一维表示),另一个用于访问行中的元素(二维数组的二维表示)。

首先,我们定义一个 Grid 类来表示这个二维网格:

public class Grid
{
    // 字段:二维整数数组
    private int[,] grid = new int[3, 3];

    // 索引器:用于访问网格的行
    public int[] this[int rowIndex]
    {
        get
        {
            // 返回指定行的数组
            return new int[] { grid[rowIndex, 0], grid[rowIndex, 1], grid[rowIndex, 2] };
        }
        set
        {
            // 设置指定行的元素
            grid[rowIndex, 0] = value[0];
            grid[rowIndex, 1] = value[1];
            grid[rowIndex, 2] = value[2];
        }
    }

    // 索引器:用于访问网格中的单个元素
    public int this[int rowIndex, int columnIndex]
    {
        get
        {
            // 返回指定行和列的元素
            return grid[rowIndex, columnIndex];
        }
        set
        {
            // 设置指定行和列的元素
            grid[rowIndex, columnIndex] = value;
        }
    }
}

在这个 Grid 类中,我们有两个索引器:

  1. 第一个索引器接受一个整数 rowIndex 作为参数,用于获取或设置网格的某一行。这个索引器返回一个整数数组,代表了网格的一行。

  2. 第二个索引器接受两个整数参数 rowIndexcolumnIndex,用于直接获取或设置网格中特定行和列的元素。

现在,我们可以创建一个 Grid 类的实例,并使用链式索引器来访问和设置网格的元素:

class Program
{
    static void Main(string[] args)
    {
        Grid myGrid = new Grid();

        // 使用链式索引器设置第一行的元素
        myGrid[0] = new int[] { 1, 2, 3 };

        // 使用链式索引器设置第二行,第三列的元素
        myGrid[1, 2] = 4;

        // 使用链式索引器获取并打印第二行的内容
        int[] row = myGrid[1];
        foreach (int value in row)
        {
            Console.Write(value + " ");
        }

        Console.WriteLine();
    }
}

在这个例子中,我们首先创建了一个 Grid 类的实例 myGrid。然后,我们使用链式索引器来设置网格的第一行和第二行第三列的值。接着,我们使用第二个索引器来获取第二行的内容,并将其打印出来。

这个例子展示了如何通过索引器的链式调用来操作一个类的不同层级的属性。这种模式在处理复杂的数据结构时非常有用,尤其是当你需要提供一个直观的方式来访问嵌套的数据时。

相关文章

  • ROC-AUC 曲线以及PRC曲线

    目录:机器学习常见面试问题汇总问题汇总(1):逻辑回归问题汇总(2):支持向量机问题汇总(3):树模型问题汇总(4...

  • 问题汇总(5):神经网络

    这篇应当也是很重要的把~ 目录:机器学习常见面试问题汇总问题汇总(1):逻辑回归问题汇总(2):支持向量机问题汇总...

  • 问题汇总(6):EM算法

    这个现学现卖把,先把链接放上来: 目录:机器学习常见面试问题汇总问题汇总(1):逻辑回归问题汇总(2):支持向量机...

  • 问题汇总(3):树模型

    好,又来到一个重难点区域,前进! 目录:机器学习常见面试问题汇总问题汇总(1):逻辑回归问题汇总(2):支持向量机...

  • 问题汇总(7):朴素贝叶斯

    这个东西也是得好好搞清楚才行~ 目录:机器学习常见面试问题汇总问题汇总(1):逻辑回归问题汇总(2):支持向量机问...

  • 【趁早变美】30天挑战实验

    每日任务汇总

  • 2020重启计划Day23今日复盘

    【早睡早起】 【每日三件事】 01 瑜珈早课学习 02 完成工作任务 汇总了关于填表方面被问到的主要问题,整理编写...

  • 无标题文章

    C#总结 在这个月的C#学习中,我遇到了很多问题,都寻求老师和同学解决掉了,在上课方面...

  • .NET 平台从入门到上台(5)

    有关 C# 语言的汇总 注释: 普通注释 //, 特殊注释 ///, 块注释 /**/. 外部引用: 使用 usi...

  • 2017年9月、10月学习总结

    1.问题: 9.11-10.12 学习c#阶段 10.13-10.27 学习oracle数据库阶段 1) 学...

网友评论

      本文标题:c#学习 每日问题汇总 2024-03-11

      本文链接:https://www.haomeiwen.com/subject/yfdmzdtx.html