MulticastDelegate.GetInvocationList() выделяет. Это можно обойти?
Можно ли вызвать MulticastDelegate
и обработать возвращаемое значение каждого подключенного обработчика без выделения памяти?
Фон
В схеме нормальных вещей Delegate[]
выделено MulticastDelegate.GetInvocationList()
незначительно. Однако в некоторых случаях важно минимизировать распределение. Например, во время игры в играх для Xbox, работающих на компактной платформе, каждый выделенный 1 МБ вызовет сбор и неприятную заминку частоты кадров.
После использования CLR Profiler для выявления и устранения большинства выделений во время игры у меня остается 50% мусора, вызванного вызовами обратного вызова Farseer Physics. Необходимо перебрать полный список вызовов, так как пользователь может вернуть false
отменить ответ о столкновении от любого из подключенных обработчиков (и вызвать делегат без использования GetInvocationList()
просто возвращает результат последнего присоединенного обработчика).
Для справки вот код Farseer, о котором идет речь:
// Report the collision to both participants. Track which ones returned true so we can
// later call OnSeparation if the contact is disabled for a different reason.
if (FixtureA.OnCollision != null)
foreach (OnCollisionEventHandler handler in FixtureA.OnCollision.GetInvocationList())
enabledA = handler(FixtureA, FixtureB, this) && enabledA;
1 ответ
Я нашел для себя решение в виде замены a на aList<Delegate>
.
мне нужно было позвонитьGetInvocationList()
на несколько кадров каждый кадрMulticastDelegate
.
private Action[] _actions;
public event Action ActionsEvent
{
add
{
if (_actions == null)
_actions = new Action[] { value };
else
{
int lastIndex = _actions.Length;
Array.Resize(ref _actions, _actions.Length + 1);
_actions[lastIndex] = value;
}
}
remove
{
if (_actions == null)
return;
int index = Array.IndexOf(_actions, value);
if (index < 0)
return false;
int newLength = _actions.Length - 1;
if (newLength > 0)
{
Action[] newActions = new Action[newLength];
if (index > 0)
Array.Copy(_actions, 0, newActions, 0, index);
if (index < newLength)
Array.Copy(_actions, index + 1, newActions, index, newLength - index);
_actions = newActions;
}
else
_actions = null;
}
}
Либо оптимизированная версия (но имеет существенные отличия от стандартной). Если вам нуженDelegate
-подобное поведение, то используйте первый вариант.
private List<Action> _actions;
public event Action ActionsEvent
{
add
{
if (_actions == null)
_actions = new List<Action>();
_actions.Add(value);
}
remove
{
if (_actions != null)
{
_actions.Remove(value);
if (_actions.Count == 0)
_actions= null;
}
}
}