2014-01-17 2 views
0

Я хочу использовать список для хранения действий в фреймворке при выполнении асинхронных операций.
Затем, когда структура синхронизируется, я зациклирую список и выполняю действия.Временное хранение делегата для вызова позже

Есть ли способ сделать это, с этим в фокусе:
- Без отражений/Динамический ввод.
- Не создавать классы/структуры для каждого нового метода вызова.
- Тип сейфа.
- хранить действия в списке.
- хранить различные виды методов и параметров.
- Выполните список позже.

Причина, по которой я не хочу использовать отражение, заключается в том, что это проблема производительности. Он будет использоваться очень часто.
В этом случае это связано с игрой, но код может использоваться как всенаправленный, и был бы превосходным для многопоточности, если бы отражения/DynamicInvoke можно было избежать.

Если это невозможно, то есть другие приятные альтернативы?

Я сделал пример кода, но он использует отражение и не является безопасным.
В основном этапы:
1. Заполните список с помощью методов с несколькими различными параметрами.
2. Прокрутите список & выполните все методы с параметрами.
3. Очистите список для следующего цикла.

{ 
    struct MyDelayedCaller 
    { 
     public Delegate TheTarget; 
     public object[] MyParameters; 

     public MyDelayedCaller(Delegate target, object[] parameters) 
     { 
      TheTarget = target; 
      MyParameters = parameters; 
     } 
    } 

    List<MyDelayedCaller> Temporary = new List<MyDelayedCaller>(); 
    void Update() 
    { 
     //something happened and another class needs to know 
     //but it will have to wait for the sync so as to not cause any treading problems 

     Temporary.Add(new MyDelayedCaller(new DelDoSomething1(DoSomething1), new object[] { 10, false })); 
     Temporary.Add(new MyDelayedCaller(new DelDoSomething1(DoSomething1), new object[] { 11, true })); 

     Temporary.Add(new MyDelayedCaller(new DelDoSomething3(DoSomething3), new object[] { "Some text" })); 
     Temporary.Add(new MyDelayedCaller(new DelDoSomething2(DoSomething2), new object[] { 1, 9999, 0.4f })); 
    } 
    void Sync() 
    { 
     foreach (var item in Temporary) 
     { 
      item.TheTarget.DynamicInvoke(item.MyParameters); 
     } 
     Temporary.Clear(); 
    } 

    delegate void DelDoSomething1(int index, bool alive); 
    void DoSomething1(int index, bool alive) 
    { 

    } 
    delegate void DelDoSomething2(int index, int amount, float scale); 
    void DoSomething2(int index, int amount, float scale) 
    { 

    } 
    delegate void DelDoSomething3(string text); 
    void DoSomething3(string text) 
    { 

    } 
} 

ответ

0

Я хотел бы пойти со следующим:

  1. IMyDelayedCaller интерфейс:

    interface IMyDelayedCaller 
    { 
        void Invoke(); 
    } 
    
  2. Набор MyDelayedCaller общие классы:

    class MyDelayedCaller<T1> : IMyDelayedCaller 
    { 
        private Action<T1> _target; 
        public T1 _param; 
    
        public MyDelayedCaller(Action<T1> target, T1 parameter) 
        { 
         _target = target; 
         _param = parameter; 
        } 
    
        public void Invoke() 
        { 
         _target(_param); 
        } 
    } 
    
    class MyDelayedCaller<T1, T2> : IMyDelayedCaller 
    { 
        private Action<T1, T2> _target; 
        public T1 _param1; 
        public T2 _param2; 
    
        public MyDelayedCaller(Action<T1, T2> target, T1 param1, T2 param2) 
        { 
         _target = target; 
         _param1 = param1; 
         _param2 = param2; 
        } 
    
        public void Invoke() 
        { 
         _target(_param1, _param2); 
        } 
    } 
    

    Я только показавшие до 2, вы можете сделать больше, если вам нужно.

  3. Изменить свой список List<IMyDelayedCaller>:

    List<IMyDelayedCaller> Temporary = new List<IMyDelayedCaller>(); 
    
  4. Добавление элементов в список с типобезопасности время компиляции:

    Temporary.Add(new MyDelayedCaller<int, bool>(DoSomething1, 10, true)); 
    Temporary.Add(new MyDelayedCaller<string>(DoSomething3, "Some text")); 
    
  5. Invoke метод с использованием интерфейса:

    foreach (var item in Temporary) 
    { 
        item.Invoke(); 
    } 
    Temporary.Clear(); 
    

Вы можете сделать остановку 4.проще, предоставляя статический класс, который позволит вашим параметрам типа будут выведены компилятором:

static class MyDelayedCaller 
{ 
    public static MyDelayedCaller<T1> Create<T1>(Action<T1> target, T1 param) 
    { 
     return new MyDelayedCaller<T1>(target, param1); 
    } 

    public static MyDelayedCaller<T1, T2> Create<T1, T2>(Action<T1, T2> target, T1 param1, T2 param2) 
    { 
     return new MyDelayedCaller<T1, T2>(target, param1, param2); 
    } 
} 

и использование:

Temporary.Add(MyDelayedCaller.Create(DoSomething1, 10, true)); 
Temporary.Add(MyDelayedCaller.Create(DoSomething3, "Some text")); 
+0

Не могли бы вы заменить этот интерфейс на «Action»? – usr

+0

@usr Вы не можете получить из «Action». Но вы можете сделать список «Список ». Я понял, что после публикации этого ответа и поэтому я добавил еще один вариант с этим решением. – MarcinJuraszek

+1

Это аккуратно + возможность добавлять дополнительную информацию в MyDelayCaller, чтобы узнать, когда и кто вызвал ее для отладки и т. Д. – Eli

0
You could queue up your inputs and actions in parallel collections: 



      var actions = new Dictionary<int, Func<object[], object>>(); 
      var inputs = new Dictionary<int, object[]>(); 

      //when you want to store the action and it's input 
      int counter = 0; 
      object[] someObjects = new object[] {}; 
      actions.Add(counter, x => { return x[0]; }); 
      inputs.Add(counter, someObjects); 
      counter++; 


      //and then later when it's time to execute 
      foreach (var input in inputs) 
      { 
       actions[input.Key].Invoke(input.Value); 
      } 

В качестве альтернативы, вы можете свернуть класс, который хранит как вход и действие, поэтому вход связан с действием во время выполнения чем-то иным, чем соответствующий словарь словаря.

+0

Это один из способов сделать это, но это не безопасный тип. Спасибо за предложение. – Eli

0

В моем другом ответе показан довольно сложный способ сделать это. Однако есть намного легче. Почему бы вам не сделать свой список List<Action>?

List<Action> Temporary = new List<Action>(); 
void Update() 
{ 
    //something happened and another class needs to know 
    //but it will have to wait for the sync so as to not cause any treading problems 

    Temporary.Add(() => DoSomething1(1, true)); 
    Temporary.Add(() => DoSomething3("Some text")); 
} 
void Sync() 
{ 
    foreach (var item in Temporary) 
    { 
     item.Invoke(); 
    } 
    Temporary.Clear(); 
} 

Должно работать нормально.

+0

Это отлично, именно то, что мне нужно. Однако я получаю странную, возможно ошибку оптимизации, когда пишу цикл for for (int i = 0; i <4; i ++) {Temporary.Add (() => DoSomething (i, true)); } int для DoSomething получает значение 4 для каждого DoSomething. Если я сделаю int a = i; & передайте a вместо i в DoSomething, тогда это правильное значение. Мне это кажется, я могу сделать неприятную ошибку, не осознавая этого ... любые идеи для этого? – Eli

+0

Используйте мой другой ответ, чтобы избавиться от этой возможности. – MarcinJuraszek

2

Надеюсь, я понимаю вопрос, потому что ответ кажется таким простым: просто храните List<Action>. Вы можете положить туда, где хотите. Вы можете перечислить список и вызвать все.

Вы абсолютно можете добавить в список такие параметры: () => DoSomething1(10, false). Параметры упакованы внутри Action. Компилятор C# генерирует (строго типизированный) класс замыкания и делает все это для вас.

Не делает ли это то, что вы хотите?

+0

Имеет неприятную ошибку с «оптимизацией», когда она используется внутри цикла, вы должны объявить новое значение foreach итерации, которое рано или поздно может привести к некоторой неприятной охоте за ошибками, когда произойдет что-то неожиданное. кроме этого, это хороший ответ. – Eli

+0

C# 5.0 исправил эту ловушку, fyi. Во всяком случае, эта запаздывающая инфраструктура вызывающего абонента взрывает код на 10 раз и решает, насколько я понимаю, только эту одиночную ловушку. И вводит, вероятно, другие ... Если вы хотите хранить информацию об отладке, используйте класс-оболочку: 'class Callable {Action Action; объект DebugInfo; } '. Простота - хорошая цель дизайна. – usr

+0

Это еще одна веская причина для переноса проектов на C# 5.0. Тем не менее, я действительно не вижу, как это ударяет код, если я могу сделать ошибку и забыть использовать Create (...), и вместо этого введите фактический метод, но это может произойти, если не считать того, что я делаю, насколько я знаю, но я не вижу альтернативы многопоточности без блокировок, распространяющихся вокруг кода, таких как мухи ... В любом случае, спасибо за информация, высоко ценится. – Eli

0

@Eli, вы получаете 4 в качестве входного значения в цикле for, потому что выполнение действий отложено. Когда они фактически выполняются, i = 4;

Чтобы избежать этого, поставите в очередь свои входы.

Я бы ответил на ваш комментарий с комментарием, но у меня пока нет репутации.

+0

Спасибо, что объяснил это мне, но я отвечу MarcinJuraszek первым, у него нет этой проблемы, ожидающей, чтобы это произошло. – Eli

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