2014-01-17 58 views
0

我想在使用異步操作時使用列表作爲存儲動作的框架。
然後,當框架同步後,我將循環列表並執行操作。臨時存儲代理稍後調用

有沒有辦法做到這一點,以此爲焦點:
- 無反射/ DynamicInvoke。
- 不爲每個要調用的新方法創建類/結構。
- 類型安全。
- 將操作存儲在列表中。
- 存儲不同種類的方法和參數。
- 稍後執行列表。

我不想使用反射的原因是因爲它是一個性能問題。這會非常頻繁地使用。
在這種情況下,它與遊戲相關,但代碼可以用作全能,並且如果可以避免reflection/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接口:

    MyDelayedCaller
    interface IMyDelayedCaller 
    { 
        void Invoke(); 
    } 
    
  2. 泛型類:

    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. 調用使用接口方法:

    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'派生。但是你可以使列表'List '。我意識到,發佈這個答案後,這就是爲什麼我增加了另一個,這個解決方案。 – 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); 
      } 

或者,你可以滾存儲輸入和A類動作,因此輸入在執行時與除匹配的字典鍵之外的其他動作耦合。

+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

這是非常好的,正是我需要的。然而,當我爲(int i = 0; i <4; i ++)寫一個for循環時,我得到一個奇怪的,可能是一個優化錯誤。 }對於每個DoSomething,DoSomething的int設置爲4。如果我做一個int a = i;並通過一個而不是我到DoSomething,那麼它是正確的價值。在我看來,我可以在沒有意識到的情況下發出一個令人討厭的錯誤......對此有何想法? – Eli

+0

使用我的其他答案擺脫這種可能性。 – MarcinJuraszek

2

我希望我能理解這個問題,因爲答案似乎很簡單:只需存儲一個List<Action>即可。你可以把你想要的東西放在那裏。您可以枚舉列表並調用所有內容。

您絕對可以將參數化的呼叫添加到這樣的列表中:() => DoSomething1(10, false)。參數包裝在Action的內部。 C#編譯器生成一個(強類型)閉包類,併爲你做所有這些。

這不是你想要的嗎?

+0

在循環中使用「優化」時會產生一個令人討厭的錯誤,您必須在每次迭代時聲明一個新值,遲早會導致一些令人討厭的錯誤捕捉,以防意外發生。除此之外,這是一個很好的答案。 – 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