Производительность конструктора TextRange в RichTextBox

В настоящее время я работаю над проектом выделения синтаксиса и завершения кода, а также над созданием пользовательского управления на основе RichTextBox. У меня были некоторые проблемы с адаптацией к тому, как работает RTB, и тому подобное, но мне удалось сделать простую подсветку синтаксиса.

Простой означает, что я выделяю весь текст каждый раз, когда пользователь вводит символ. Он не должен быть быстрым или что-то в этом роде, но он слишком медленный. Проблемы с производительностью становятся видимыми, когда у меня есть около 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, переработать объект «диапазон» или что-то в этом роде? Какие еще есть решения.


person Dejan Maksimovic    schedule 25.07.2012    source источник
comment
Кстати, если TextRanges ctor занимает столько времени (внутренне это очень мягко), я подозреваю, что ваш colorInterval метод вызывается довольно часто!   -  person user7116    schedule 25.07.2012
comment
Для обычного текста он вызывается n / 10 раз, если n - длина текста, на его вызов не нужно так много времени. Так, например, если текст имеет длину около 5000 символов, для его обработки требуется 1 секунда и 500 вызовов colorInterval - и построение занимает 80% + времени, это должно быть - без конструктора - 0,2 секунды, что является временем, когда я можно было смириться. Так я не могу. Я хотел попробовать сделать Span вместо TextRange, но он не строится из-за какой-то глупой ошибки и все вылетает ...   -  person Dejan Maksimovic    schedule 25.07.2012


Ответы (1)


Самый простой способ - (как вы предлагаете) повторно использовать объект 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);
  ...
}

(Обратите внимание: проверка нулевой ссылки перед принятием решения об инициализации переменной не так удобна, как создание экземпляра TextRange в объявлении. К сожалению, TextRange не имеет общедоступного пустого конструктора, а TextPointer вообще не имеет общедоступных конструкторов. Вы можете создать его с помощью некоторых фиктивные значения в конструкторе вашего класса, чтобы избежать этой проверки.)

Выше я сказал «если это действительно конструктор». Очевидно, что правильно выполненное профилирование выделило конструктор, но с такой же легкостью это может быть обычная процедура для конструктора и метода Select.

Предполагая, что вы не вызываете colorInterval из более чем одного потока, я бы сказал, что это лучший подход, чем у вас в настоящее время, независимо от экономии времени, потому что (я предполагаю, что) colorInterval вызывается часто, и постоянное создание и сборка мусора последующие TextRange объекты, которые он оставляет после себя, безусловно, неэффективны.

Сделав это предложение, я настоятельно рекомендую вам отойти от модели, в которой вы сканируете весь документ каждый раз, когда вы хотите отреагировать (например) на изменение одного символа. Предполагая, что вы нацеливаетесь на> = .net 3.5, RichTextBox предоставляет событие TextChanged, которое сообщает список TextChange объектов, из которых вы можете определить местоположение (и символы, добавленные или удаленные) изменений.

Естественно, здесь будет некоторая работа, потому что любое изменение вряд ли полностью инкапсулирует выделенный диапазон. В классе TextRange есть метод поиска абзацев, в которых можно найти начало и конец диапазона, если это помогает. Вероятно, есть случай для хранения деталей каждого выделенного диапазона, чтобы вы могли быстро проверить пересечение.

person Bob Sammers    schedule 25.07.2012
comment
Хорошо, я попробовал Select, и это не помогло, потому что, я думаю, он все еще работает как конструктор. Однако я использовал таймер, который немного улучшил время ввода для текста размером до нескольких тысяч символов, поэтому это приемлемо, поскольку наш текст не будет слишком длинным. Я также хочу делать что-то с интервалами, чтобы мне не приходилось обновлять весь документ, поэтому я обязательно сделаю его немного лучше, но я хотел знать, есть ли более простой способ заставить все работать. Спасибо за ответ, я уже начал волноваться, никто мне не поможет. :) - person Dejan Maksimovic; 25.07.2012
comment
Нет проблем - я был просто удивлен, обнаружив вопрос, на который еще никто не ответил! Понятия не имею, почему - мне это кажется совершенно хорошим и хорошо сформулированным вопросом. - person Bob Sammers; 27.07.2012