【问题标题】:Storing a list of Actions with parameters and later executing them存储带有参数的动作列表,然后执行它们
【发布时间】:2018-06-12 23:10:33
【问题描述】:

我有一个操作的枚举,我想运行:

public enum theActions
{
    action1,
    action2
}

我想将它们存储在字典中:

public Dictionary<theActions, Action> _theActions { get; }

_theActions = new Dictionary<theActions, Action>
{
    [theActions.action1] = () => action1Func()
};

对于每个操作,我都有自己的功能:

public void action1Func(int inParam)
{
    //do whatever
}

稍后,我需要调用其中一个函数:

public void execAction(int inVar, Action action) 
{ 
    //inVar isn't the parameter I want to pass to the action. It's used, for something else.
    action(); 
}

execAction(1, _theActions[theActions.action1]);

我不确定,如何更改我的代码以使 Action 在任何地方都接受参数,如果我需要一个不需要参数的操作怎么办?我是否必须在该函数中添加一个虚拟参数?

我知道了,到目前为止:

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public enum theActions
        {
            action1,
            action2
        }

        public Dictionary<theActions, Action<int>> _theActions { get; }

        public void execAction(int inVar, Action<int> action)
        {
            //inVar isn't the parameter I want to pass to the action. It's used, for something else.
//            action();
        }

        public Form1()
        {
            InitializeComponent();

            _theActions = new Dictionary<theActions, Action<int>>
            {
                [theActions.action1] = (Action<int>)((int x) => action1Func(x))
            };

        }

        public void action1Func(int inParam)
        {
            //do whatever
            MessageBox.Show($"Hello ... inParam : {inParam}");
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //This works manually
            _theActions[theActions.action1].Invoke(12);

            //But, I want the execAction to work
            //execAction(1, _theActions[theActions.action1]);
        }
    }
}

它可以手动调用它。我只需要帮助进入 execAction() 并运行它。所以,关闭。

【问题讨论】:

  • 我认为您对如何实现这一点感到困惑,因为您对自己真正想做的事情感到困惑。如果你不知道一个方法需要哪些参数,你将如何构建一种方法来提供它们?如果你的 executor 方法总是使用一个 int,那么如果这个 action 需要两个字符串,你会怎么做?
  • 在这种情况下,它始终是单个 int 参数。
  • 然后将您的操作设为Action&lt;int&gt;
  • 我在所有 Action 语句之后添加 。我收到错误。
  • 你有初始化时的参数信息[theActions.action1] = () =&gt; action1Func() 还是在调用动作时有这些参数?

标签: c# .net action


【解决方案1】:
public void execAction(int someInt, Action action)
{
    action();

    // or: action.Invoke();
}

您应该能够在初始化中删除整个 lambda,因为您已经有一个带有一个 int 参数的方法:

public Form1()
{
    InitializeComponent();

    _theActions = new Dictionary<theActions, Action<int>>
    {
        [theActions.action1] = action1Func
    };
}

带有特定参数的调用如下所示:

int yourParameter = 12345;
execAction(42, () => (_theActions[theActions.action1]).Invoke(yourParameter));

【讨论】:

  • 其实... execAction 需要使用传入的参数执行Actions。抱歉,无法解释。我不是很聪明。
  • 另外,我将存储一堆动作,我需要随机执行。所以,我希望 execAction(1,whateveraction) 逻辑工作。注意:传入的第一个参数是函数中的东西,而不是我需要传递给动作的参数。
  • 恐怕我不明白你在问什么。你需要更具体。
  • 我想使用函数execAction,它接受一个单独的int参数和一个动作,需要用它自己的参数传入,所以,,,就像..execAction(1,anAction( aIntParameter))。就我而言,动作需要存储在字典中。我想在 execAction() 中将 _theActions[theActions.action1] 传入一个参数,该参数将被执行。
  • 所以将yourParameterToPass 作为第三个参数传递给execAction
【解决方案2】:

尝试删除字典并改用它:

public void action1Func(int p1) //action 1 has one int parameter
{
}
public void action2Func() //let's consider action 2 takes no parameters
{
}
public void action3Func(int p1, int p2, int p3)
{
}
// Order of parameters was changed to leverage implicit null parameters for convenience
// See invocations below
public void execAction(theActions action, 
                       int? inVar1 = null, int? inVar2 = null, int? inVar3 = null) 
{
    switch (action) // Based on action kind, invoke the right function.
    {               // The mapping is now coded in a switch statement 
                    // instead of a Dictionary declaration
        case theActions.action1: action1Func(inVar1.Value); break;
        case theActions.action2: action2Func(); break;
        case theActions.action3: action3Func(inVar1.Value, inVar2.Value, inVar3.Value); break;
    }
}
// Invocations
execAction(theActions.action1, 1);
execAction(theActions.action2);
execAction(theActions.action3, 1, 3, 5);

如果对execAction正确使用隐式参数声明,则可以调用具有任意数量参数的方法。

您可以执行参数验证(例如,您可以在调用之前检查 inVar1.HasValue == true),否则如果省略参数,代码将快速失败(如果 HasValue 为 false,Nullable.Value 会抛出 InvalidOperationException)。

如果参数数量增加并且变得无法管理,您可以将它们放入参数包类中并通过构造函数验证它们的初始化。

如果您定义这些重载,您可以获得更高的安全性(编译时检查):

public void execAction1(int p1)
    => execAction(theActions.action1, p1);
public void execAction2()
    => execAction(theActions.action2);
public void execAction3(int p1, int p2, int p3)
    => execAction(theActions.action3, p1, p2, p3);

// Invocations will be
execAction1(1);
execAction2();
execAction3(1, 3, 5);

但这最后一步有点违背了目的。您可以改为调用原始方法:)。

【讨论】:

  • 您还可以声明一个 Dictionary 并强制转换为 Action,如 here 所述。但请记住,DynamicInvoke 会对性能产生严重影响。
  • Cosmin ...我知道,该怎么做。我很感激,答案。代码是正确的。但是,我想像上面那样做。如果我没有任何参数,则上面的代码有效。我可以传入动作并执行动作();如果有的话,我想修复它,用于学习目的。因为,我的第一个想法是你只需将 添加到每个动作。那没用。
  • 所以,我尝试了 DynamicInvoke()。如果我将其称为“手动”,那将起作用。但是,我需要将操作传递给函数,如上面的代码所示。但是,我看了一点,DynamicInvoke() 正如你所暗示的那样,对性能的影响太大了。我只想将我发布的代码转换为带参数的操作。
猜你喜欢
  • 1970-01-01
  • 2020-03-31
  • 2013-11-20
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-19
  • 1970-01-01
相关资源
最近更新 更多