V4002. Unity Engine. Avoid storing consecutive concatenations inside a single string in performance-sensitive context. Consider using StringBuilder to improve performance.
Анализатор обнаружил возможность оптимизации операций конкатенации внутри часто выполняемого метода.
Конкатенация приводит к созданию нового объекта строки, а значит и к дополнительному выделению памяти в управляемой куче. В целях повышения производительности этого следует избегать в часто выполняемом коде. В случае если к значению строки нужно несколько раз добавить различные фрагменты, вместо конкатенации разработчиками Unity рекомендуется использовать тип 'StringBuilder'.
Рассмотрим пример:
[SerializeField] Text _stateText;
....
void Update()
{
....
string stateInfo = ....;
....
stateInfo += ....;
stateInfo += ....;
....
stateInfo += ....;
_stateText.text = stateInfo;
....
}
Здесь реализовано построение строки 'stateInfo' путем нескольких операций конкатенации. Выполнение этого кода в методе 'Update' (вызывается несколько десятков раз в секунду), приведет к быстрому накоплению 'мусора' в памяти и к более частому вызову сборщика мусора для его очистки. Последнее может негативно отразиться на производительности. Избежать лишнего выделения памяти можно с помощью объекта 'StringBuilder':
[SerializeField] Text _stateText;
....
StringBuilder _stateInfo = new StringBuilder();
void Update()
{
_stateInfo.Clear();
....
_stateInfo.AppendLine(....);
_stateInfo.AppendLine(....);
....
_stateInfo.AppendLine(....);
_stateText.text = _stateInfo.ToString();
....
}
Метод 'Clear' очищает содержимое 'StringBuilder', но при этом не освобождает выделенную память. Таким образом, дополнительное выделение памяти потребуется только в том случае, если уже используемой будет недостаточно для хранения нового текста.
Рассмотрим другой пример:
[SerializeField] Text _text;
....
List<string> _messages = new();
....
void LateUpdate()
{
....
string message = BuildMessage();
_text.text = message;
_messages.Clear();
}
string BuildMessage()
{
string result = "";
foreach (var msg in _messages)
result += msg + "\n";
return result;
}
В этом примере сообщение, выводимое на интерфейс, формируется с помощью метода 'BuildMessage'. Так как он вызывается внутри 'LateUpdate' (так же часто, как и внутри 'Update'), его тоже стоит оптимизировать:
StringBuilder _message = new StringBuilder();
string BuildMessage()
{
_message.Clear();
foreach (var msg in _messages)
_message.AppendLine(msg);
return _message.ToString();
}