Я пытаюсь реализовать эффективный список вызовов с рекурсией и изменениями во время обработки событий.
Я знаю, что мог бы использовать делегат и операторы +=
, -=
.Я пробую альтернативный подход и хотел бы увидеть, как он сравнивается с этим.Меня не волнует многопоточность (с использованием Unity3d).
Основные проблемы:
- Производительность
- Нет выделений
- Использование памяти
Является ли этот код правильным, учитывая, что единственными вызванными методами были бы конструкторы, Subscribe
, Unsubscribe
и Send
?
Идеяза ним:
- Вызывать только обработчики, присутствующие в списке во время вызова, используя локальную
count
переменную - Вместо удаления обработчиков, заменить их пустым обработчиком
- Очистить пустые обработчики, когда вся обработка завершена (включая рекурсивную)
using System;
using System.Collections.Generic;
/// <summary>
/// Invocation list which efficiently handles recursion and subscription/unsubscription during event handling.
/// </summary>
public class InvocationList<T> : List<Action<T>>
{
enum State { None, Running, RunningDirty }
static readonly Action<T> m_emptyAction = x => { };
State m_state = State.None;
public InvocationList()
{
}
public InvocationList(int capacity)
: base(capacity)
{
}
public void Subscribe(Action<T> handler)
{
Add(handler);
}
public bool Unsubscribe(Action<T> handler)
{
if (m_state == State.None)
{
// Not sending event, just remove handler
return Remove(handler);
}
else
{
// Sending event, replace handler by empty action
int index = IndexOf(handler);
if (index >= 0)
{
this[index] = m_emptyAction;
m_state = State.RunningDirty;
return true;
}
else
{
return false;
}
}
}
/// <summary>
/// Raises the event. Handles recursion and subscription/unsubscription during event handling.
/// Event is sent only to handlers present in invocation list at the moment of call.
/// </summary>
public void Send(T value)
{
if (m_state == State.None)
{
// Non-recursive invocation
try
{
m_state = State.Running;
ProcessHandlers(value);
}
finally
{
if (m_state == State.RunningDirty)
{
// Clean up empty handlers
RemoveAll(s => s == m_emptyAction);
}
m_state = State.None;
}
}
else
{
// Recursive invocation
ProcessHandlers(value);
}
}
private void ProcessHandlers(T value)
{
// Store count, this ignores handlers added during execution
int count = Count;
for (int i = 0; i < count; i++)
{
this[i](value);
}
}
}