我目前正在进行语法高亮显示和代码完成项目,并基于RichTextBox进行用户控制。我在适应RTB的工作方式和其他方面遇到了一些问题,但我已经设法进行了简单的语法高亮显示。
Simple意味着每次用户键入字符时,我都会突出显示整个文本。它不应该很快,但它太慢了。当我有大约500个字符的文本时,性能问题就会变得明显,并且我只对每个键入的字符进行一次文本传递("colorInterval"函数在一次传递中被调用大约100次)。
性能分析表明,问题是TextRange构造函数占用了大约80%以上的时间,每次我需要为文本间隔着色时都会使用它:
private void colorInterval(TextPointer start, TextPointer end)
{
TextRange range = new TextRange(start, end);
if(isFunction(range.Text)) colorAsFunction(range);
if(isInQuotes(range.Text)) colorAsQuoted(range);
...
}
下面是我的问题:
我这样做是不是做错了什么,或者有没有办法提高TextRange的性能,回收"range"对象之类的东西?还有什么其他解决方案。
最简单的方法是(正如您所建议的)重用TextRange
对象,如果它确实是占用您大部分时间的构造函数。TextRange
属性Start
和End
是只读的,但有一个公共方法Select
将同时更新这两个属性,采用两个TextPointer
对象,就像您一直使用的构造函数一样。
protected TextRange range;
private void colorInterval(TextPointer start, TextPointer end)
{
if (range == null)
range = new TextRange(start, end);
else
range.Select(start, end);
...
}
(注意:在决定是否初始化变量之前检查null引用并不像在声明中实例化TextRange
那么简单。不幸的是,TextRange
没有公共空构造函数,TextPointer
根本没有公共构造函数。你可以在类构造函数中使用一些伪值来创建它,以避免这种检查。)
上面,我说"如果它真的是构造函数"。显然,您所做的评测突出显示了构造函数,但它也可以很容易地成为构造函数和Select
方法的通用例程。
假设您不从多个线程调用colorInterval
,无论节省多少时间,我都认为这是一种比当前更好的方法,因为(我猜)colorInterval
被频繁调用,并且它留下的后续TextRange
对象的不断创建和垃圾收集肯定是低效的。
提出这个建议后,我强烈建议您放弃每次想要对(例如)单个字符的更改做出反应时都要扫描整个文档的模式。假设您的目标是>=.net 3.5,RichTextBox
将提供一个TextChanged
事件,该事件报告TextChange
对象的列表,您可以从中计算出更改的位置(以及添加或删除的字符)。
当然,这里会有一些工作,因为任何更改都不太可能完全封装突出显示的范围。TextRange
类有一个方法,用于查找可以找到范围的开始和结束的段落,以防有帮助。可能存在存储每个高亮显示范围的详细信息的情况,这样您就可以快速检查交叉点。