C# 索引器练习总结
前言:主要是想通过写文章加深自己对C#理解,如果有写的错误的地方请指正,感激不尽。
C# 4.0 的 Visual Studio 2010 示例,中有一个索引器的的示例(http://code.msdn.microsoft.com/Indexers2-Sample-00f2f47a 源代码下载),示例主要是为了显示:C# 类如何声明索引器,以表示不同种类事物的类似数组的集合。功能实现为将一个字符串,通过分词程序,可以把字符串里面的单词识别出来,通过索引器进行相应的替换。
详见代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System; using Indexers_2; 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); } }
代码中主要有三个类,Document 类,WordCollection类和CharacterCollection类,其中WordCollection类和CharacterCollection类是Document类的内部类,分别将Document类的字符串进行处理,可以单词化呈现和字母化呈现。
单词化呈现是由WordCollection类一个核心函数实现:bool GetWord(char[] text, int begin, int wordCount, out int start, out int 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; }
函数的功能是从字符串的begin(最小合法值是0)开始,寻找第wordCount(最小合法值是0)个单词,如果存在则返回true和单词在字符串的位置start和长度length,不存在返回false。
每次从字符串查找第i个单词,WordCollection类则调用多次调用GetWord函数,效率比较低,于是我就想通过GetWord函数建立一个索引表,按照单词在字符串的起始位置和长度建立索引,这样可以方便访问字符串的单词,并对单词进行重新复制。
顺便感受一下C#的接口,内部类,父类子类有什么特性,加深自己对OO的理解。
设计接口:索引器用来返回字符串中对应的单词,Count用来返回字符串总共有多少个单词。
public interface IIndexer { string this[int index] { get; set; } int Count { get; } }
WordCollection类继承IIndexer接口,主要实现GetWord函数,WordCollection类的子类WordCollectionA,WordCollectionB实现IIndexer接口。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class WordCollectionB : WordCollection { internal class WordIndexInfo { private const int INCREMENT = 100; private int Count; private int[,] wordInfo; public int[] this[int index] { get { return new int[2] { wordInfo[index, 0], wordInfo[index, 1] }; } } private int _pCurrent; public int PCurrent { get { return _pCurrent; } } public bool AddIndexInfo(int start, int length) { if (_pCurrent + 1 >= Count) { if (!EnlargeWordInfo()) return false; } _pCurrent++; wordInfo[_pCurrent, 0] = start; wordInfo[_pCurrent, 1] = length; return true; } private bool EnlargeWordInfo() { bool OK = false; try { int[,] temp = new int[Count + INCREMENT, 2]; for (int i = 0; i <= _pCurrent; i++) { temp[i, 0] = wordInfo[i, 0]; temp[i, 1] = wordInfo[i, 1]; } Count = Count + INCREMENT; wordInfo = temp; OK = true; } catch (OutOfMemoryException e) { Console.WriteLine(e.ToString()); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } finally { OK = false; } return OK; } public void mergeIndexInfo(int index,WordIndexInfo otherOne,int startOffset) { if(otherOne._pCurrent<0||index<0) return ; //确保内存够用 while(Count<this._pCurrent+otherOne._pCurrent+1) { if(!EnlargeWordInfo()) throw new Exception(); } //对this.wordInfo中要修改之后的单词位置信息进行修改和移动 int oldPos=_pCurrent; int newPos=oldPos+otherOne._pCurrent; while(oldPos>index) { wordInfo[newPos,0]=wordInfo[oldPos,0]+startOffset; oldPos--; newPos--; } int wstart=wordInfo[index,0]; for(int i=0;i<=otherOne._pCurrent;i++) { wordInfo[index+i,0]=wstart+otherOne.wordInfo[i,0]; wordInfo[index+i,1]=otherOne.wordInfo[i,1]; } _pCurrent+=otherOne.PCurrent; } public WordIndexInfo() { wordInfo = new int[INCREMENT, 2]; Count = INCREMENT; _pCurrent = -1; } } private WordIndexInfo wIndexInfo; private void SetIndexInfo( char[] TextArray,WordIndexInfo info) { int start=0,length=0; while (GetWord(TextArray, start, 0, out start, out length)) { info.AddIndexInfo(start, length); start =start +length; } } public WordCollectionB(MyDocument d):base(d) { wIndexInfo = new WordIndexInfo(); SetIndexInfo(d.TextArray, wIndexInfo); } public override string this[int index] { get { if (!(index<0||index>wIndexInfo.PCurrent)) { return new string(document.TextArray, wIndexInfo[index][0], wIndexInfo[index][1]); } else throw new IndexOutOfRangeException(); } set { int start, length; length=wIndexInfo[index][1]; start = wIndexInfo[index][0]; WordIndexInfo _tempwii=new WordIndexInfo(); if (length == value.Length) { Array.Copy(value.ToCharArray(), 0, document.TextArray, start, length); SetIndexInfo(value.ToCharArray(),_tempwii); this.wIndexInfo.mergeIndexInfo(index,_tempwii,0); } else { value.ToCharArray(0, value.Length); 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; SetIndexInfo(value.ToCharArray(),_tempwii); this.wIndexInfo.mergeIndexInfo(index,_tempwii,value.Length-length); } } } public override int Count { get { return wIndexInfo.PCurrent + 1; } } }
WordCollectionB类中又嵌套WordIndexInfo类,用来封装字符串单词的索引表的。主要功能是:1可以动态的扩充,内存不足时可以抛出异常。2当使用索引器对单词进行替换时,可以自动的更新索引表,即,计算修改单词位置之后的单词索引的偏移量,计算相对应单词在字符串中新的start位置。3把替换的字符串分解为单词,计算它们在索引表的start位置,合并到索引表中。
无论是微软提供的算法还是我的算法都有一个bug,就是替换的字符串如果包含被替换的字符串时,就会陷入死循环中,"aa bb aa cc ac" 把单词中的"aa"替换成" aa bb aa aa "时,就会出现死循环,新的字符串如果只包含一个单词那么没有问题。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class MyDocument { public interface IIndexer { string this[int index] { get; set; } int Count { get; } } public class WordCollection:IIndexer { protected readonly MyDocument document; // 包含文档 internal WordCollection() { } public WordCollection(MyDocument d) { document = d; } protected 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 - start; return true; } inWord = -1; } } else { if (isLetter) inWord = i; } } return false; } public virtual 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)) { 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; } } } } public virtual int Count { set; get; } } public class WordCollectionA : WordCollection { public WordCollectionA(MyDocument d):base(d) { } public override 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)) { 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; } } } } public override 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 WordCollectionB : WordCollection { internal class WordIndexInfo { private const int INCREMENT = 100; private int Count; private int[,] wordInfo; public int[] this[int index] { get { return new int[2] { wordInfo[index, 0], wordInfo[index, 1] }; } } private int _pCurrent; public int PCurrent { get { return _pCurrent; } } public bool AddIndexInfo(int start, int length) { if (_pCurrent + 1 >= Count) { if (!EnlargeWordInfo()) return false; } _pCurrent++; wordInfo[_pCurrent, 0] = start; wordInfo[_pCurrent, 1] = length; return true; } private bool EnlargeWordInfo() { bool OK = false; try { int[,] temp = new int[Count + INCREMENT, 2]; for (int i = 0; i <= _pCurrent; i++) { temp[i, 0] = wordInfo[i, 0]; temp[i, 1] = wordInfo[i, 1]; } Count = Count + INCREMENT; wordInfo = temp; OK = true; } catch (OutOfMemoryException e) { Console.WriteLine(e.ToString()); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } finally { OK = false; } return OK; } public void mergeIndexInfo(int index,WordIndexInfo otherOne,int startOffset) { if(otherOne._pCurrent<0||index<0) return ; //确保内存够用 while(Count<this._pCurrent+otherOne._pCurrent+1) { if(!EnlargeWordInfo()) throw new Exception(); } //对this.wordInfo中要修改之后的单词位置信息进行修改和移动 int oldPos=_pCurrent; int newPos=oldPos+otherOne._pCurrent; while(oldPos>index) { wordInfo[newPos,0]=wordInfo[oldPos,0]+startOffset; oldPos--; newPos--; } int wstart=wordInfo[index,0]; for(int i=0;i<=otherOne._pCurrent;i++) { wordInfo[index+i,0]=wstart+otherOne.wordInfo[i,0]; wordInfo[index+i,1]=otherOne.wordInfo[i,1]; } _pCurrent+=otherOne.PCurrent; } public WordIndexInfo() { wordInfo = new int[INCREMENT, 2]; Count = INCREMENT; _pCurrent = -1; } } private WordIndexInfo wIndexInfo; private void SetIndexInfo( char[] TextArray,WordIndexInfo info) { int start=0,length=0; while (GetWord(TextArray, start, 0, out start, out length)) { info.AddIndexInfo(start, length); start =start +length; } } public WordCollectionB(MyDocument d):base(d) { wIndexInfo = new WordIndexInfo(); SetIndexInfo(d.TextArray, wIndexInfo); } public override string this[int index] { get { if (!(index<0||index>wIndexInfo.PCurrent)) { return new string(document.TextArray, wIndexInfo[index][0], wIndexInfo[index][1]); } else throw new IndexOutOfRangeException(); } set { int start, length; length=wIndexInfo[index][1]; start = wIndexInfo[index][0]; WordIndexInfo _tempwii=new WordIndexInfo(); if (length == value.Length) { Array.Copy(value.ToCharArray(), 0, document.TextArray, start, length); SetIndexInfo(value.ToCharArray(),_tempwii); this.wIndexInfo.mergeIndexInfo(index,_tempwii,0); } else { value.ToCharArray(0, value.Length); 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; SetIndexInfo(value.ToCharArray(),_tempwii); this.wIndexInfo.mergeIndexInfo(index,_tempwii,value.Length-length); } } } public override int Count { get { return wIndexInfo.PCurrent + 1; } } } public class WordCollectionC : WordCollection { internal class WordIndexInfo { private const int INCREMENT = 100; private int Count; private int[,] wordInfo; public int[] this[int index] { get { return new int[2] { wordInfo[index, 0], wordInfo[index, 1] }; } } private int _pCurrent; public int PCurrent { get { return _pCurrent; } set { _pCurrent = value; } } public bool AddIndexInfo(int start, int length) { if (_pCurrent + 1 >= Count) { if (!EnlargeWordInfo()) return false; } _pCurrent++; wordInfo[_pCurrent, 0] = start; wordInfo[_pCurrent, 1] = length; return true; } private bool EnlargeWordInfo() { bool OK = false; try { int[,] temp = new int[Count + INCREMENT, 2]; for (int i = 0; i <= _pCurrent; i++) { temp[i, 0] = wordInfo[i, 0]; temp[i, 1] = wordInfo[i, 1]; } Count = Count + INCREMENT; wordInfo = temp; OK = true; } catch (OutOfMemoryException e) { Console.WriteLine(e.ToString()); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } finally { OK = false; } return OK; } public WordIndexInfo() { wordInfo = new int[INCREMENT, 2]; Count = INCREMENT; _pCurrent = -1; } } private WordIndexInfo wIndexInfo; private void SetIndexInfo(char[] TextArray, WordIndexInfo info) { int start = 0, length = 0; while (GetWord(TextArray, start, 0, out start, out length)) { info.AddIndexInfo(start, length); start = start + length; } } private int WordCount(char[] TextArray) { int count = 0; int length=0, start = 0; while(GetWord(TextArray,start+length,0,out start,out length)) { count++; } return count; } public WordCollectionC(MyDocument d):base(d) { wIndexInfo = new WordIndexInfo(); SetIndexInfo(d.TextArray, wIndexInfo); _count = wIndexInfo.PCurrent + 1; } public override string this[int index] { get { if (index<_count) { if (index <= wIndexInfo.PCurrent) return new string(document.TextArray, wIndexInfo[index][0], wIndexInfo[index][1]); else { int start, length; start = 0; length =0; while (wIndexInfo.PCurrent < index) { if (GetWord(document.TextArray, start + length, 0, out start, out length)) { wIndexInfo.AddIndexInfo(start, length); } else { throw new IndexOutOfRangeException(); } } return new string(document.TextArray, wIndexInfo[index][0], wIndexInfo[index][1]); } } else { throw new IndexOutOfRangeException(); } } set { int start, length; length=wIndexInfo[index][1]; start = wIndexInfo[index][0]; if (length == value.Length) { Array.Copy(value.ToCharArray(), 0, document.TextArray, start, length); } else { _count=wIndexInfo.PCurrent +WordCount( value.ToCharArray(0, value.Length)); 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; wIndexInfo.PCurrent = index - 1; } } } public override int Count { get { return _count; } } private int _count; } // 以下类型允许以类似字符数组的方式查看文档 // : public class CharacterCollection { readonly MyDocument document; // 包含文档 internal CharacterCollection(MyDocument 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 IIndexer Words; public CharacterCollection Characters; private char[] TextArray; // 文档的文本。 public MyDocument(string initialText) { TextArray = initialText.ToCharArray(); Words = new WordCollectionC(this); Characters = new CharacterCollection(this); } public string Text { get { return new string(TextArray); } } }
总结代码书写中的经验
1 接口和抽象类的关系与不同
http://www.cnblogs.com/lovemyth/archive/2008/09/08/828909.html 这个是比较好的答案,至于我的理解1接口可以由抽象类(或者父类集成),然后由不同的子类实现。2接口的成员都是public,且public和private,protected不能修饰接口定义的东西。3接口是定义了可以什么,抽象类定义了你是什么
2 内部类的好处
http://www.cnblogs.com/qingyuan/archive/2010/08/25/1808177.html ,我的理解 1内部类可以访问外部类,即使是外部类的private的 2内部类更好的封装了外部类的一些东西。
3 abstract和 virtual 的不同
我的理解两者都要用Override去实现,但是virtual不强制,abstract必须实现。
4 父类子类实例化时对应的构造函数
base很重要,通过base调用父类的相关构造函数,默认是无参数的。
5 声明二维数组时int[,]与int[][]的不同点
一个是矩阵一个是交错数组。
2013年4月8日23:33:08
菜包子 于宿舍