2015-08-23 3 views
-3

У меня следующий сценарий У меня есть цикл, который имеет обратный вызов Update, который несколько объектов используют CreateDelegate для создания своих собственных делегатов и добавления себя к нему во время выполнения. теперь я хочу избежать создания N делегатов для N объектов, поэтому я подумал, что с помощью многократного открытого делегата я могу создать один делегат добавить все экземпляры в него и вызвать его, Как это сделать через отражение Я создаю делегат, используя CreateDelegate из моей информации о методе один раз, а затем вызовите NewMultiCastDelegate через отражение и отправьте ему список объектов, но все будет выглядеть так, как только я вызову вызову с другим фиктивным экземпляром, чтобы вызвать его, выдается исключение незащищенной памяти.добавить делегатов и вызвать MulticastDelegate путем отражения

Как я могу это сделать?

Я не могу сделать это без отражения, поскольку методы моих объектов будут известны во время выполнения, и любой, у кого есть метод Update, должен быть вызван этим менеджером циклов.

Это для игрового движка

+0

Вы можете уточнить немного больше, что вы пытаетесь сделать? Это не похоже на все, что требовало бы делегатов или размышлений. – Luaan

+0

Я подражаю движению движка единства, поэтому нужно делать это с отражением и тому подобное. двигатель находит методы, такие как Update() и LateUpdate() через отражение без каких-либо интерфейсов/базовых классов. – asm

+0

Ну, вы отметили, что объекты регистрируются сами - и они, очевидно, знают себя во время компиляции. Поэтому им просто нужно передать делегат себе - то, что вы делаете с этим делегатом, зависит от вас (например, чтобы сделать делегат многоадресной рассылки, вы можете просто использовать '+ ='). Но, в конце концов, всегда будет много делегатов, и они всегда будут вызываться в цикле один за другим - это не похоже на то, что у CLR есть магический метод, который позволяет ему одновременно запускать всех делегатов; он просто выполняет их последовательно. – Luaan

ответ

0

UPDATE:

Есть две проблемы, связанные с использованием MulticastDelegate + открытых делегатов экземпляра.

  1. Невозможно вызвать открытые делегаты экземпляра с несколькими несвязанными типами. Возьмите типы A и B, каждый с методом Update. Открытый делегат экземпляра A.Update может быть вызван с экземплярами A (или типами, производными от A), но не с экземплярами B. Это связано с тем, что A.Update и B.Update не связаны со средой выполнения .NET.
  2. Несколько открытых делегатов экземпляров могут быть объединены в один 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"); 
     } 
    } 
} 
+0

Я так делаю так, я подражаю поведению игрового движка Unity, который делает это с отражением и вот так. Я смог создать один делегат на объект после поиска метода, а затем назначить его делегату в цикле (у меня есть эта работа на самом деле), но я хочу избежать создания одного делегата на объект (Delegate.CreateDelegate) – asm

+0

Вы не можете просто дать сырые объектов в MulticastDelgate. Почему вы не хотите создавать экземпляры делегата? Давление на сборщик мусора? Вы можете ссылаться на экземпляры MethodInfo непосредственно при публикации обновлений. –

+0

Конечно, события * являются * делегатами многоадресной передачи. Хотя я согласен с тем, что они обеспечивают более удобный интерфейс (в частности, не позволяя влиять на других делегатов в многоадресной рассылке). Тем не менее, для чего-то вроде этого, я не думаю, что их вполне достаточно - вам нужно как-то очистить объекты после их смерти. – Luaan

Смежные вопросы