如何使用.NET中的Action执行一个具有未知数量参数的方法?

24

我想在工作线程上执行一些操作,同时向用户显示进度条。我已经创建了一个类

public class ProgressBar
{
   public void StartAsyncTask(Action action)
   {
      Task t = new Task(action);
      t.start();
   }
}

我发现可以通过以下方式将任何方法发送到StartAsyncTask

  ProgressBar pb = new ProgressBar();
  pb.StartAsyncTask( () => DoSomething(15, "something"));

  public void DoSomething(int i, string s)
   {
      //do something
   }

首先,我好像不太理解什么是lambda表达式 - () => - 以及如何翻译,以及如何将Action对象传递给一个具有未知参数数量的委托。

我想使用BackgroundWorker来控制我的进度条,但在这种情况下,我需要调用该操作。所以类似这样:

void m_backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    Action action = e.Argument as Action; //same action here passed through EventArgs
   //but already in a worker thread so no need for the Task object 

   //and now i need to somehow invoke the action object but i don't know what the parameters are. 

   action.Invoke( ? ); 
}

在第一个示例中,如何在不知道StartAsyncTask(Action action)方法的参数的情况下执行操作?

为什么在这种情况下调用操作时需要知道参数?

关于如何/为什么/何时使用"Action"的所有内容对我来说都很不清楚,即使我阅读了MSDN文档和其他一些帖子。任何关于此的信息都将对我有所帮助。


在调用函数时,您需要知道参数。否则(如果可以不传递参数),该函数将没有任何参数传递给它。因此,使您的第一个“parameter.Something”调用抛出“NullReferenceException”。 - Simon Whitehead
2个回答

56

我认为你有点过度思考了,让我们从头开始:

  1. lambda表达式是引用方法执行的一种表示法。例如:

     x => x + 3
    

    在最基本的层面上,这代表一个函数,它接受一个输入值x,然后返回一个值等于x + 3。因此在你的情况下,你的表达式:

    () => DoSomething(15, "Something")
    

    表示一个不带参数的方法,然后调用方法DoSomething(15, "Something")。编译器在幕后为您将其转换为FuncAction委托。因此,实际上是这样的:

    new Action(delegate()
    {
        DoSomething(15, "Something")
    }); 
    

    我简单表达式的编译器改写如下:

    new Func<int, int>(delegate(int x)
    {
        return x + 3;
    });
    
  2. 接下来,如果您想稍后调用某个操作,则执行此操作的语法非常简单:

  3. Action someMethod = new Action(() => { Console.WriteLine("hello world"); }));
    someMethod(); // Invokes the delegate
    

    如果你有一个指定的Action实例,只需使用()语法调用它即可,因为Action是一个不带参数且返回值为空的委托。

    函数同样很容易:

    Func<int, int> previousGuy = x => x + 3;
    var result = previousGuy(3); // result is 6
    
  4. 最后,如果你想传递一个调用方法,但此时没有参数的上下文,你可以简单地将调用包装在一个操作中,稍后再调用。例如:

  5. var myAction = new Action(() =>
         {
              // Some Complex Logic
              DoSomething(15, "Something");
              // More Complex Logic, etc
         });
    
    InvokeLater(myAction);
    
    public void InvokeLater(Action action)
    {
          action();
    }
    

    你的方法内部产生了一个闭包,从而捕获了所有的数据并保存。如果你能够在事件中传递一个带有 e.Argument 属性的 Action,那么你只需要调用 (e.Argument as Action)()


2

你不能在该委托上使用DynamicInvoke()(它需要params object[] args作为参数)。

action.DynamicInvoke(arg1, arg2, arg3 );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接