临时存储一个代表以便稍后调用

时间:2014-01-17 22:37:17

标签: c# multithreading .net-4.0 visual-c#-express-2010

我希望在进行异步操作时使用列表作为向框架存储操作 然后,当框架同步时,我将循环列表并执行操作。

有没有办法做到这一点,重点关注:
  - No Reflection / 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)
    {

    }
}

5 个答案:

答案 0 :(得分:2)

我希望我理解这个问题,因为答案看起来很简单:只需存储一个List<Action>。你可以在那里放任何你想要的东西。您可以枚举列表并调用所有内容。

您绝对可以将参数化调用添加到此类列表中:() => DoSomething1(10, false)。参数打包在Action内。 C#编译器生成一个(强类型)闭包类,并为您完成所有这些。

这不是你想做的吗?

答案 1 :(得分: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. 使用界面方法调用:

    foreach (var item in Temporary)
    {
        item.Invoke();
    }
    Temporary.Clear();
    
  6. 您可以通过提供静态类来使停止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"));
    

答案 2 :(得分: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);
            }

或者,您可以滚动一个存储输入和操作的类,因此输入在执行时通过匹配的字典键以外的其他操作耦合到操作。

答案 3 :(得分: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();
}

应该工作得很好。

答案 4 :(得分:0)

@Eli,你在for循环中输入4作为输入值,因为动作的执行是延迟的。当它们实际执行时,i = 4;

要避免这种情况,请排列输入。

我会以评论回复你的评论,但还没有代表。