我理解Action表示要对某个对象执行的操作,但是在一些代码库中,我看到了声明为Action<Action<T>>
的参数,并且难以理解要对另一个操作执行的操作意味着什么。
从概念上讲,Action<Action<T>>
代表什么,你可以建议一个你会使用它的例子吗?
答案 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 );
}