V4003. Unity Engine. Avoid capturing variable in performance-sensitive context. This can lead to decreased performance.
Анализатор обнаружил захват переменной в лямбда-выражении внутри часто выполняемого метода. Захват переменных может приводить к снижению производительности из-за дополнительного выделения памяти.
Рассмотрим пример:
void Update()
{
....
List<int> numbers = GetNumbers();
int divisor = GetDivisor();
var result = numbers.Select(x => x / divisor);
....
}
Здесь 'Update' — метод Unity, выполняющий покадровое обновление. Метод 'Update' — часто вызываемый, и его не рекомендуется нагружать лишними операциями.
В приведенном примере используется лямбда-выражение с захватом переменной 'divisor'. Как упоминалось ранее, захват переменной из внешнего контекста приводит к дополнительному созданию объекта.
Таким образом, представленный участок кода создает дополнительную нагрузку на GC.
Оптимальная реализация метода может выглядеть следующим образом:
void Update()
{
....
List<int> numbers = GetNumbers();
int divisor = GetDivisor();
var result = new List<int>(numbers.Count);
for (int i = 0; i < numbers.Count; i++)
{
result.Add(numbers[i]/divisor);
}
....
}
Использование собственной реализации, аналогичной 'Select', позволяет избавиться от дополнительного выделения памяти и тем самым снизить нагрузку на GC.
Рассмотрим еще один пример:
void Update()
{
....
List<int> numbers = GetNumbers();
int divisor = GetDivisor();
if (AreAllMultipleOf(numbers, divisor))
....
}
bool AreAllMultipleOf(List<int> lst, int divisor)
{
return lst.All(elem => elem % divisor == 0);
}
Здесь из метода 'Update' вызывается метод 'AreAllMultipleOf', который определяет, являются ли все полученные числа кратными значению 'divisor'. Так же, как и ранее: 'Update' — часто вызываемый метод, выполняющий покадровое обновление в Unity.
В представленном случае метод 'AreAllMultipleOf' регулярно выполняется внутри 'Update', а значит, также является часто вызываемым.
Метод 'AreAllMultipleOf' для выполнения проверки использует лямбда-выражение с захватом переменных. Это приводит к дополнительному выделению памяти, которое может негативно сказаться на производительности приложения.
Оптимальная реализация может выглядеть следующим образом:
void Update()
{
....
List<int> numbers = GetNumbers();
int divisor = GetDivisor();
if (AreAllMultipleOf(numbers, divisor))
....
}
bool AreAllMultipleOf(List<int> lst, int divisor)
{
foreach (int num in lst)
{
if (num % divisor != 0)
return false;
}
return true;
}
Здесь мы в очередной раз воспользовались собственной реализацией, что позволило избежать дополнительного выделения памяти и снизить нагрузку на сборщик мусора.