使用操作和任务
Working with Actions and Task
我想传入一个 returns void 并接受 参数 int 的方法。我该怎么做?
谁能告诉我非 lambda 和 lambda 版本?
private void LongTask(int s)
{
Thread.Sleep(s* 1000);
}
public void Run()
{
Task q = Task.Run(Action<int>(LongTask));
}
在您的示例中,您没有显示要传递给方法的值。这让你很难确定你到底在问什么。但我 认为 你要求说明如何调用你的方法 LongTask()
,将适当的值传递给它作为 Task.Run()
操作,并提供这个同时使用 lambda 语法和非 lambda 语法的插图。
注意: 其中很少一部分是 Task.Run()
特有的。当您处理将委托传递给方法时,这一切都适用,在这种情况下,委托需要表示具有与您正在调用的方法中使用的签名不同的签名的方法。这里涉及Task.Run()
几乎是偶然的。
总之……
实际上,由于 lambda 表达式(在此用法中)编译为匿名方法,因此实际上有三个选项:
"Old-school"匿名方法:
public void Run()
{
int someValue = 17;
Task q = Task.Run(delegate { LongTask(someValue); });
}
以上导致编译器创建匿名方法 (delegate() { LongTask(someValue); }
),并生成代码以创建委托实例以传递给 Task.Run()
方法。委托的类型是从用法中推断出来的(即基于与匿名方法声明匹配的一个方法重载)。
Lambda 匿名方法:
public void Run()
{
int someValue = 17;
Task q = Task.Run(() => LongTask(someValue));
}
上面的例子和前面的例子一样,只是使用名义上更简洁的lambda语法来声明匿名方法。我说 "nominally" 是因为在这种特殊情况下,它并没有那么短。但是在其他情况下,lambda 语法要方便得多;通常,当匿名方法 returning 一个值时,因为使用 lambda 语法 return
语句是隐式的。
显式:
class A
{
private readonly int _value;
private readonly Action<int> _action;
public A(int value, Action<int> action)
{
_value = value;
_action = action;
}
public void M() { _action(_value); }
}
public void Run()
{
int someValue = 17;
Task q = Task.Run((Action)(new A(someValue, LongTask).M));
}
以上内容依赖于显式编写的 class 来达到与匿名方法选项导致的编译器生成的 class 相同的目的。根据具体情况,编译器生成的 class class 在实现上可能会有很大的不同。但行为本质上是相同的:创建 class 的实例,其中存储所需的值,并且 class 中包含适当签名的方法,其中该方法是您要执行的代码。
(旁白:在这种情况下,为了简洁起见,我展示了一个实际采用委托实例的实现,而不是对目标对象和方法调用进行硬编码。这实际上是一个更比编译器实际生成的可重用实现,主要是因为编译器必须处理更复杂的场景,这是一种优化,只在非常狭窄的场景中有用,即只有一个方法被调用。编译器也会有存储 this
并将匿名方法的主体 复制 到生成的方法,使用存储的 this
引用和任何其他需要的捕获变量,例如调用 LongTask()
方法。)
在上面的变量 _value
是 readonly
,但在匿名方法中,它们通常是从匿名方法的上下文中捕获的可变变量。为了更好地模拟匿名方法通常如何工作,您需要更像这样的东西:
class A
{
public int value;
private readonly Action<int> _action;
public A(int value, Action<int> action)
{
this.value = value;
_action = action;
}
public void M() { _action(value); }
}
public void Run()
{
A a = new A(17, LongTask);
Task q = Task.Run((Action)a.M);
a.value = 19;
}
请注意,在上面,value
是一个 public 字段。而代码实际上是在调用Task.Run()
之后修改了变量。这类似于在匿名方法示例中更改 someValue
的值(同样,在调用 Task.Run()
之后),并且具有相同的风险:如果 Task.Run()
方法之后的赋值在之前执行任务可以执行方法调用,然后传递给方法的值与任务可以先执行方法的值不同。
道德: 始终注意您捕获的变量,并确保它们具有您期望的生命周期和价值。
顺便说一句,在最后两个示例中需要强制转换为所需的委托类型 Action
,否则 Task.Run()
到 select 的重载对编译器来说是不明确的,在 [=33= 之间] 和 Task.Run(Func<Task>)
。简短版本:当尝试从方法组转换为委托类型并将其与特定重载匹配时,委托签名的 return 类型将被忽略。有关较长的版本,请参阅 Compiler Ambiguous invocation error - anonymous method and method group with Func<> or Action。
我想传入一个 returns void 并接受 参数 int 的方法。我该怎么做?
谁能告诉我非 lambda 和 lambda 版本?
private void LongTask(int s)
{
Thread.Sleep(s* 1000);
}
public void Run()
{
Task q = Task.Run(Action<int>(LongTask));
}
在您的示例中,您没有显示要传递给方法的值。这让你很难确定你到底在问什么。但我 认为 你要求说明如何调用你的方法 LongTask()
,将适当的值传递给它作为 Task.Run()
操作,并提供这个同时使用 lambda 语法和非 lambda 语法的插图。
注意: 其中很少一部分是 Task.Run()
特有的。当您处理将委托传递给方法时,这一切都适用,在这种情况下,委托需要表示具有与您正在调用的方法中使用的签名不同的签名的方法。这里涉及Task.Run()
几乎是偶然的。
总之……
实际上,由于 lambda 表达式(在此用法中)编译为匿名方法,因此实际上有三个选项:
"Old-school"匿名方法:
public void Run()
{
int someValue = 17;
Task q = Task.Run(delegate { LongTask(someValue); });
}
以上导致编译器创建匿名方法 (delegate() { LongTask(someValue); }
),并生成代码以创建委托实例以传递给 Task.Run()
方法。委托的类型是从用法中推断出来的(即基于与匿名方法声明匹配的一个方法重载)。
Lambda 匿名方法:
public void Run()
{
int someValue = 17;
Task q = Task.Run(() => LongTask(someValue));
}
上面的例子和前面的例子一样,只是使用名义上更简洁的lambda语法来声明匿名方法。我说 "nominally" 是因为在这种特殊情况下,它并没有那么短。但是在其他情况下,lambda 语法要方便得多;通常,当匿名方法 returning 一个值时,因为使用 lambda 语法 return
语句是隐式的。
显式:
class A
{
private readonly int _value;
private readonly Action<int> _action;
public A(int value, Action<int> action)
{
_value = value;
_action = action;
}
public void M() { _action(_value); }
}
public void Run()
{
int someValue = 17;
Task q = Task.Run((Action)(new A(someValue, LongTask).M));
}
以上内容依赖于显式编写的 class 来达到与匿名方法选项导致的编译器生成的 class 相同的目的。根据具体情况,编译器生成的 class class 在实现上可能会有很大的不同。但行为本质上是相同的:创建 class 的实例,其中存储所需的值,并且 class 中包含适当签名的方法,其中该方法是您要执行的代码。
(旁白:在这种情况下,为了简洁起见,我展示了一个实际采用委托实例的实现,而不是对目标对象和方法调用进行硬编码。这实际上是一个更比编译器实际生成的可重用实现,主要是因为编译器必须处理更复杂的场景,这是一种优化,只在非常狭窄的场景中有用,即只有一个方法被调用。编译器也会有存储 this
并将匿名方法的主体 复制 到生成的方法,使用存储的 this
引用和任何其他需要的捕获变量,例如调用 LongTask()
方法。)
在上面的变量 _value
是 readonly
,但在匿名方法中,它们通常是从匿名方法的上下文中捕获的可变变量。为了更好地模拟匿名方法通常如何工作,您需要更像这样的东西:
class A
{
public int value;
private readonly Action<int> _action;
public A(int value, Action<int> action)
{
this.value = value;
_action = action;
}
public void M() { _action(value); }
}
public void Run()
{
A a = new A(17, LongTask);
Task q = Task.Run((Action)a.M);
a.value = 19;
}
请注意,在上面,value
是一个 public 字段。而代码实际上是在调用Task.Run()
之后修改了变量。这类似于在匿名方法示例中更改 someValue
的值(同样,在调用 Task.Run()
之后),并且具有相同的风险:如果 Task.Run()
方法之后的赋值在之前执行任务可以执行方法调用,然后传递给方法的值与任务可以先执行方法的值不同。
道德: 始终注意您捕获的变量,并确保它们具有您期望的生命周期和价值。
顺便说一句,在最后两个示例中需要强制转换为所需的委托类型 Action
,否则 Task.Run()
到 select 的重载对编译器来说是不明确的,在 [=33= 之间] 和 Task.Run(Func<Task>)
。简短版本:当尝试从方法组转换为委托类型并将其与特定重载匹配时,委托签名的 return 类型将被忽略。有关较长的版本,请参阅 Compiler Ambiguous invocation error - anonymous method and method group with Func<> or Action。