C#索引属性用法实例分析
本文通过一个具体的C#示例,演示如何定义和使用索引属性(Indexers)。文章讲解了如何将文档数据封装在自定义类中,并通过不同的索引器提供按单词或按字符的类似数组的访问方式。
C#索引属性核心概念
在C#中,索引属性允许类的实例像数组一样被索引访问。通常我们使用 this[int index] 语法来定义它们。虽然标准类库如 List<T> 已经提供了丰富的功能,但在需要自定义数据存储结构或特定访问逻辑时,手动实现索引属性非常有价值。
下面的实例展示了如何在 Document 类中创建两种不同类型的索引属性:一种用于访问单个字符,另一种用于访问由空格分隔的单词。
完整代码实现与分析
该示例定义了 Document 类,它包含一个私有字符数组 TextArray 来存储文本。为了提供不同的访问粒度,内部嵌套了两个类:
- CharacterCollection:直接映射到字符数组,实现简单的字符级索引。
- 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 实例。随后:
- 遍历所有单词,将名为 "peter" 的单词全部替换为 "penelope"。
- 遍历所有字符,将所有小写 'p' 替换为大写 'P'。
- 最后输出修改后的完整文本。
总结
本例展示了C#如何通过索引属性(Indexers)将自定义集合类伪装成数组行为。这种做法不仅提高了代码的可读性,还使得对复杂数据结构(如分词后的文本)的操作更加直观自然。以上就是C#索引属性用法实例分析的详细内容,更多关于C#面向对象设计的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













