UPDATE:
Есть две проблемы, связанные с использованием MulticastDelegate + открытых делегатов экземпляра.
- Невозможно вызвать открытые делегаты экземпляра с несколькими несвязанными типами. Возьмите типы
A
и B
, каждый с методом Update
. Открытый делегат экземпляра A.Update
может быть вызван с экземплярами A
(или типами, производными от A
), но не с экземплярами B
. Это связано с тем, что A.Update
и B.Update
не связаны со средой выполнения .NET.
- Несколько открытых делегатов экземпляров могут быть объединены в один MulticastDelegate, но это не подходит для вашего прецедента. Вы хотите вызвать метод
Update
для каждого экземпляра в списке. Вызов MulticastDelegate будет последовательно вызывать каждый делегат в своем списке вызовов с тем же списком параметров. Таким образом, MulticastDelegate с несколькими открытыми делегатами экземпляра может использоваться для вызова списка методов для одного экземпляра, но не одного метода в списке экземпляров. Имеет ли это смысл?
Я предполагаю, что вы пытаетесь ускорить добавление компонента Unity в моторном эмуляторе, и именно поэтому вы хотите, чтобы прекратить называть CreateDelegate
для каждого из них.
Вместо использования MulticastDelegate вы можете добиться ускорения путем создания открытых экземпляров делегатов при первом добавлении типа, а затем кеширования этих делегатов для повторного использования.
Вот код вдоль этих линий:
public class ComponentEventAggregator
{
public void AddComponent(object component)
{
var typeGroup = this.GetTypeGroup(component.GetType());
typeGroup.AddComponent(component);
}
public void Update()
{
foreach (var typeGroup in this.typeGroupsByType.Values)
{
typeGroup.Update();
}
}
public void RemoveComponent(object component)
{
TypeGroup typeGroup;
if (this.typeGroupsByType.TryGetValue(component.GetType(), out typeGroup))
{
typeGroup.RemoveComponent(component);
}
}
private TypeGroup GetTypeGroup(Type type)
{
TypeGroup typeGroup;
if (!this.typeGroupsByType.TryGetValue(type, out typeGroup))
{
typeGroup = TypeGroup.Create(type);
this.typeGroupsByType[type] = typeGroup;
}
return typeGroup;
}
private abstract class TypeGroup
{
public abstract void Update();
// ... LateUpdate, OnApplicationPause, etc.
public abstract void AddComponent(object component);
public abstract void RemoveComponent(object component);
// Create a TypeGroup<T> for the specified type
public static TypeGroup Create(Type type)
{
var closedDelegatesType = typeof(TypeGroup<>).MakeGenericType(new[] { type });
var typeDelegates = closedDelegatesType.GetConstructor(Type.EmptyTypes).Invoke(new object[0]);
return (TypeGroup)typeDelegates;
}
}
private class TypeGroup<T> : TypeGroup where T : class
{
public TypeGroup()
{
this.update = CreateOpenDelegate("Update");
this.lateUpdate = CreateOpenDelegate("LateUpdate");
this.onApplicationPause = CreateOpenDelegate<bool>("OnApplicationPause");
// ...other Unity events
}
public override void Update()
{
if (this.update != null)
{
foreach (var component in this.components)
{
this.update(component);
}
}
}
public override void AddComponent(object component)
{
var t = component as T;
if (t != null)
{
this.components.Add(t);
}
}
public override void RemoveComponent(object component)
{
var t = component as T;
if (t != null)
{
this.components.Remove(t);
}
}
private static Action<T> CreateOpenDelegate(string functionName)
{
var method = GetMethod(functionName, Type.EmptyTypes);
if (method == null)
{
return null;
}
return (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), method);
}
private static Action<T, TArg1> CreateOpenDelegate<TArg1>(string functionName)
{
var method = GetMethod(functionName, new[] { typeof(TArg1) });
if (method == null)
{
return null;
}
return (Action<T, TArg1>)Delegate.CreateDelegate(typeof(Action<T, TArg1>), method);
}
private static MethodInfo GetMethod(string functionName, Type[] parameterTypes)
{
return typeT.GetMethod(functionName, BindingFlags.Instance | BindingFlags.Public, null, parameterTypes, null);
}
private readonly Action<T> update;
private readonly Action<T> lateUpdate;
private readonly Action<T, bool> onApplicationPause;
private List<T> components = new List<T>();
private static readonly Type typeT = typeof(T);
}
private Dictionary<Type, TypeGroup> typeGroupsByType = new Dictionary<Type, TypeGroup>();
}
Это дает прирост скорости 50X, чтобы добавить компонент по сравнению с Delegate.CreateDelegate
на моем компьютере.
раз Тестовые с 1 млн A
и 1 млн B
экземпляров:
10892ms add closed delegates with CreateDelegate for all Components
11ms Update via the closed delegates
187ms add all Components to the type-based aggregator
30ms Update via the open instance delegates (i.e. Update on aggregator)
Update
метод как A
и B
приращений частный статический Int.
ORIGINAL:
Похоже, вы бы лучше обнажая C# событие на вашем менеджере цикла и добавления подписки, которые вызывают методы обновления. Альтернативно, все типы с помощью метода Update могут реализовывать интерфейс IUpdater, тогда вы отправляете список экземпляров IUpdater в диспетчер циклов вместо списка объектов.
Предполагая, что вы на самом деле есть некоторые другие хорошие причины для использования отражения ...
class Program
{
public class Updater
{
public void AddUpdateListeners(object[] listeners)
{
Func<object, MethodInfo> getUpdateMethod = l =>
l.GetType()
.GetMethod("Update", BindingFlags.Instance | BindingFlags.Public, null, new Type[0], null);
listeners
.Select(listener => new { listener, update = getUpdateMethod(listener) })
.Where(l => l.update != null)
.Select(l => Delegate.CreateDelegate(typeof(UpdateDelegate), l.listener, l.update))
.OfType<UpdateDelegate>()
.ToList()
.ForEach(d => this.updateDel += d);
}
public void PublishCallbacks()
{
var handler = this.updateDel;
if (handler != null)
{
handler();
}
}
private delegate void UpdateDelegate();
private UpdateDelegate updateDel;
}
static void Main(string[] args)
{
var updater = new Updater();
updater.AddUpdateListeners(new object[] { new A(), new B(), });
updater.PublishCallbacks();
}
public class A
{
public void Update()
{
Console.WriteLine("A updated");
}
}
public class B
{
public void Update()
{
Console.WriteLine("B updated");
}
}
}
Вы можете уточнить немного больше, что вы пытаетесь сделать? Это не похоже на все, что требовало бы делегатов или размышлений. – Luaan
Я подражаю движению движка единства, поэтому нужно делать это с отражением и тому подобное. двигатель находит методы, такие как Update() и LateUpdate() через отражение без каких-либо интерфейсов/базовых классов. – asm
Ну, вы отметили, что объекты регистрируются сами - и они, очевидно, знают себя во время компиляции. Поэтому им просто нужно передать делегат себе - то, что вы делаете с этим делегатом, зависит от вас (например, чтобы сделать делегат многоадресной рассылки, вы можете просто использовать '+ ='). Но, в конце концов, всегда будет много делегатов, и они всегда будут вызываться в цикле один за другим - это не похоже на то, что у CLR есть магический метод, который позволяет ему одновременно запускать всех делегатов; он просто выполняет их последовательно. – Luaan