从概念上讲,Action <action <t>&gt;是什么?代表</动作<T>

时间:2011-05-13 00:37:33

标签: c#

我理解Action表示要对某个对象执行的操作,但是在一些代码库中,我看到了声明为Action<Action<T>>的参数,并且难以理解要对另一个操作执行的操作意味着什么。

从概念上讲,Action<Action<T>>代表什么,你可以建议一个你会使用它的例子吗?

4 个答案:

答案 0 :(得分:6)

从最后开始,我已经使用此模式创建了一个多线程“进度”对话框,而长任务在主线程上运行。我调用我的进度对话框创建函数,其中lambda用于完成工作,它接收一个知道如何更新进度条和状态文本的lambda。像这样:

LongRunningOperation.Run((update)=>
  {
    update("phase 1");
    // do work for phase 1

    update("phase 2");
    // do work for phase 2
  });

所以我的Run函数声明如下:

public static void Run(Action<Action<string>> worker);

泛化,任何从函数本身接收lambda的worker lambda都将使用这种模式。

答案 1 :(得分:3)

Action<Action<T>>是一个以Action<T>为参数的委托。这将允许您传递一个委托,当被调用时,将传递第二个委托(类型为Action<T>)。

然而,

这种情况的使用将是(或应该)非常罕见。通常情况下,您只需使用一个Action<T>并传递它。我可以看到这个被使用的唯一地方是一个场景,例如在集合中存储一组委托。

例如,假设您有一系列对Person类起作用的操作。这可以定义为:List<Action<Person>> actions;鉴于此,如果您想使用List<T>.ForEach对某些特定人员执行所有这些方法,只要符合某些条件,您可以执行以下操作:

Person person = GetPersion();
actions.ForEach( action => 
   {
       if (person.Foo)
            action(person); // Call the action on the person
   });

上述lambda将是Action<Action<Person>>。 (但我个人不会编写这样的代码,而是建议使用正常的foreach循环 - 这只是为了说明目的来演示如何产生这种情况......)

答案 2 :(得分:2)

不要将其视为要对其他操作执行的操作,而应将其视为将另一个操作作为参数的操作。例如,您可能有一个操作,即作业是将另一个操作排入队列以便稍后执行:

class WorkProcessor
{
    public Action<Action<WorkItem>> WorkScheduler { get; set; }

    public void ScheduleWork(WorkItem workItem)
    {
        WorkScheduler(ProcessWork);
    }

    public void ProcessWork(workItem)
    {
        //...
    }
}

答案 3 :(得分:0)

Action<Action<T>>是一个(委托给a)函数,它将另一个(委托给a)函数作为其参数。这样的“高阶函数”可以多次调用它的参数,有条件地,使用日志记录等。这样的函数允许你封装程序逻辑并传递它。

这是一个简单的例子,其中选择是否记录函数调用是在Main()中进行的,然后该选择的实现对于实际的worker函数是不可见的;而不是传递配置标志,你传递一个封装选择的更高阶函数。

void Run( Action<int> fn, int x )
{
   fn(x);
}

void RunAndLog( Action<int> fn, int x )
{
    print( "before " + x );
    fn(x);
    print( "after " + x );
}

void InvokeWorkerTenTimes( Action<Action<int>> gn, Action<int> fn )
{ 
    // fn is WHAT to do
    // gn is HOW to do it!
    for( int i=0; i<10; i++ )
        gn(fn, i);
}

void DoWork(int x)
{
}    

void Main()
{
    if( LoggingEnabled )
        InvokeWorkerTenTimes( RunAndLog, DoWork );
    else
        InvokeWorkerTenTimes( Run, DoWork );     
}