C#索引属性用法实例分析

宋时安
宋时安
发布时间:2026-10-13 | 5人浏览过
原创

本文通过一个具体的C#示例,演示如何定义和使用索引属性(Indexers)。文章讲解了如何将文档数据封装在自定义类中,并通过不同的索引器提供按单词或按字符的类似数组的访问方式。

C#索引属性核心概念

在C#中,索引属性允许类的实例像数组一样被索引访问。通常我们使用 this[int index] 语法来定义它们。虽然标准类库如 List<T> 已经提供了丰富的功能,但在需要自定义数据存储结构或特定访问逻辑时,手动实现索引属性非常有价值。

下面的实例展示了如何在 Document 类中创建两种不同类型的索引属性:一种用于访问单个字符,另一种用于访问由空格分隔的单词。

完整代码实现与分析

该示例定义了 Document 类,它包含一个私有字符数组 TextArray 来存储文本。为了提供不同的访问粒度,内部嵌套了两个类:

  1. CharacterCollection:直接映射到字符数组,实现简单的字符级索引。
  2. WordCollection:包含复杂的辅助函数 GetWord,用于在文本中查找并返回指定索引处的单词。
// indexedproperty.cs
using System;

public class Document
{
  // 以下类型允许文档的查看方式与字的数组一样:
  public class WordCollection
  {
    readonly Document document; // 包含文档
    internal WordCollection(Document d)
    {
      document = d;
    }
    // Helper 函数 -- 从字符"begin"开始在字符数组"text"中搜索
    // 字数"wordCount"。如果字数小于 wordCount,
    // 则返回 false。将"start"和
    // "length"设置为单词在文本中的位置和长度:
    private bool GetWord(char[] text, int begin, int wordCount, out int start, out int length) 
    { 
      int end = text.Length;
      int count = 0;
      int inWord = -1;
      start = length = 0; 
      for (int i = begin; i <= end; ++i) 
      {
        bool isLetter = i < end && Char.IsLetterOrDigit(text[i]);
        if (inWord >= 0) 
        {
          if (!isLetter) 
          {
            if (count++ == wordCount) 
            {
              start = inWord;
              length = i - inWord;
              return true;
            }
            inWord = -1;
          }
        }
        else 
        {
          if (isLetter)
            inWord = i;
        }
      }
      return false;
    }
    // 获取和设置包含文档中的字的索引器:
    public string this[int index] 
    {
      get 
      { 
        int start, length;
        if (GetWord(document.TextArray, 0, index, out start, out length))
          return new string(document.TextArray, start, length);
        else
          throw new IndexOutOfRangeException();
      }
      set 
      {
        int start, length;
        if (GetWord(document.TextArray, 0, index, out start, out length)) 
        {
          // 用字符串"value"替换位于 start/length 处的
          // 字:
          if (length == value.Length) 
          {
            Array.Copy(value.ToCharArray(), 0, document.TextArray, start, length);
          }
          else 
          {
            char[] newText = 
              new char[document.TextArray.Length + value.Length - length];
            Array.Copy(document.TextArray, 0, newText, 0, start);
            Array.Copy(value.ToCharArray(), 0, newText, start, value.Length);
            Array.Copy(document.TextArray, start + length, newText, start + value.Length, document.TextArray.Length - start - length);
            document.TextArray = newText;
          }
        }          
        else
          throw new IndexOutOfRangeException();
      }
    }
    // 获取包含文档中字的计数:
    public int Count 
    {
      get 
      { 
        int count = 0, start = 0, length = 0;
        while (GetWord(document.TextArray, start + length, 0, out start, out length))
          ++count;
        return count; 
      }
    }
  }
  // 以下类型允许文档的查看方式像字符的"数组"
  // 一样:
  public class CharacterCollection
  {
    readonly Document document; // 包含文档
    internal CharacterCollection(Document d)
    {
     document = d; 
    }
    // 获取和设置包含文档中的字符的索引器:
    public char this[int index] 
    {
      get 
      { 
        return document.TextArray[index]; 
      }
      set 
      { 
        document.TextArray[index] = value; 
      }
    }
    // 获取包含文档中字符的计数:
    public int Count 
    {
      get 
      { 
        return document.TextArray.Length; 
      }
    }
  }
  // 由于字段的类型具有索引器,
  // 因此这些字段显示为"索引属性":
  public WordCollection Words;
  public CharacterCollection Characters;
  private char[] TextArray; // 文档的文本。
  public Document(string initialText)
  {
    TextArray = initialText.ToCharArray();
    Words = new WordCollection(this);
    Characters = new CharacterCollection(this);
  }
  public string Text 
  {
    get 
    { 
      return new string(TextArray); 
    }
  }
}

class Test
{
  static void Main()
  {
    Document d = new Document(
      "peter piper picked a peck of pickled peppers. How many pickled peppers did peter piper pick?"
    );
    // 将字"peter"更改为"penelope":
    for (int i = 0; i < d.Words.Count; ++i) 
    {
      if (d.Words[i] == "peter") 
        d.Words[i] = "penelope";
    }
    // 将字符"p"更改为"P"
    for (int i = 0; i < d.Characters.Count; ++i) 
    {
      if (d.Characters[i] == 'p')
        d.Characters[i] = 'P';
    }
    Console.WriteLine(d.Text);
  }
}

关键逻辑拆解

  • 单词定位算法 (GetWord):该私有方法遍历字符数组,识别字母数字字符作为单词的一部分。当检测到单词边界(非字母数字字符)且达到目标单词索引时,返回该单词的起始位置 start 和长度 length。
  • 安全访问:如果在 get 或 set 操作中无法找到对应的单词或超出范围,均抛出 IndexOutOfRangeException,确保程序安全性。
  • 动态调整数组:在 WordCollection.set 中,如果新单词长度与原单词不同,代码会创建一个新大小的字符数组,并使用 Array.Copy 分段复制数据,以维持文本连续性。

测试用例说明

在 Main 方法中,首先创建一个包含经典绕口令文本的 Document 实例。随后:

  1. 遍历所有单词,将名为 "peter" 的单词全部替换为 "penelope"。
  2. 遍历所有字符,将所有小写 'p' 替换为大写 'P'。
  3. 最后输出修改后的完整文本。

总结

本例展示了C#如何通过索引属性(Indexers)将自定义集合类伪装成数组行为。这种做法不仅提高了代码的可读性,还使得对复杂数据结构(如分词后的文本)的操作更加直观自然。以上就是C#索引属性用法实例分析的详细内容,更多关于C#面向对象设计的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表