【问题标题】:Task.Run how to write it with action and ref variable nameTask.Run 如何用 action 和 ref 变量名编写它
【发布时间】:2014-09-21 21:29:42
【问题描述】:

我站在一个“小”的语法问题,不知道如何正确地写出我想要的。

我有以下方法:

public void DoSomeMagic(string foo, ref string bar)
{
    //DoSomeMagic...
}

现在我想在 Task.Run 中卸载此代码,我通常会编写以下内容:

public async void Button_Click(object sender, EventArgs args)
{
    string foo = "Hello Foo";
    string bar = "Hello Bar";

    await Task.Run(() => DoSomeMagic(foo, ref bar));
}

这不会编译告诉我:“不能在匿名方法体内使用 'ref' 或 'out' 参数 'bar'”

所以我想为什么还要使用 () =>,因为我只是在调用该方法,我可以将其简化为:

Task.Run(DoSomeMagic(foo, ref bar));

这又不能编译告诉我:无法解析方法'Run(void)',候选者是:Run(Action) 和 Run(Func)

所以同样没问题,Visual Studio 你的需求就是我的命令。

并将代码更改为:

Task.Run((Action)DoSomeMagic(foo, ref bar));

再次无法编译告诉我:无法将“void”类型的表达式转换为“Action”类型,

好吧,这开始变得棘手了......

我没有尝试返回 void,而是尝试 int 并强制转换为 Func 给我错误:无法将类型“int”的表达式转换为类型“Func”

看到那个来了,但我想试试看:

所以我尝试了以下方法:

public Action CallDoSomeMagic(string foo, ref string bar)
{
    //DoSomeMagic...
    return new Action(() => DoSomeMagic(foo, ref bar));
}

Task.Run(CallDoSomeMagic);

但这又给了我消息“不能在匿名方法体内使用'ref'或'out'参数'bar'”

由于每次尝试我的头痛都在增加,我想你们可以帮助我。 有没有可能?

【问题讨论】:

  • 阅读错误信息:“不能在匿名方法体中使用 'ref' 或 'out' 参数 'bar'” - bar 是“ref 参数”。你不能在 lambda 中使用它。所以...不要!
  • @MarcGravell 它很棘手,因为我想使用 ref,你是在告诉我它不可能吗?
  • 不可能,有两个原因 - 我会添加一个答案...
  • 不确定我是否应该将此作为答案,但我怀疑您是否会看到这项工作。由于您可以使用异步等待来操纵控制流,因此您将能够在您等待的部分之间的整个过程中创建完全虚假的逻辑来操纵您的参数。很确定这就是他们不允许这样做的原因
  • @AndreasMüller 确实;这里最简单的“修复”是:而不是传递 ref string,而是传递对 SomeTypeThatHasAStringProperty 对象的引用 - 然后只需访问 obj.Bar 或其他:完成(将其编辑到我的答案中)

标签: c# syntax task-parallel-library task


【解决方案1】:

正如消息所说:你不能那样做。

您可以获取参数值的副本,然后捕获那个,例如:

public Action CallDoSomeMagic(string foo, ref string bar)
{
    var snapshot = bar;
    return new Action(() => DoSomeMagic(foo, ref snapshot));
}

但请注意,snapshot 的更新在通过bar 调用的外部是不可见的。

原因有两个:

首先,用于 lambda 的捕获变量成为编译器生成的上下文类的字段。这适用于非 ref/out 参数,因为它们已经具有复制语义。所以在我的snapshot 例子中,这实际上是:

var ctx = new CompilerGeneratedContextClassWithHorribleName();
ctx.foo = foo;
ctx.snapshot = bar;
return new Action(ctx.CompilerGeneratedMethod);

CompilerGeneratedMethod 在哪里:

DoSomeMagic(foo, ref snapshot);

这对于 refs 是不可能的,因为您基本上需要该字段作为对字符串引用的引用,这……很混乱。

但更重要的是:考虑调用者的生命周期。这可能是:

void SomeMethod() {
    string s = "abc";
    CallDoSomeMagic("def", ref s);
}

请特别注意,即使代理在很久以后被调用,代码也需要工作 - 实际上我们可能期望它在您的情况下,因为它涉及 Taskasync。现在:如果SomeMethod 已退出:那个指向字符串引用的引用在哪里? 提示:它只是堆栈上的任意位置,现在超出范围.


只是为了提供一个更简单的解决方法:考虑传递SomeType obj,而不是传递ref string bar,其中obj.Bar 是您想要的string;即

public Action CallDoSomeMagic(string foo, SomeType obj)
{
    return new Action(() => DoSomeMagic(foo, obj));
}

public void DoSomeMagic(string foo, SomeType obj)
{
    // read and write obj.Bar here
}

请注意,如果需要,您也可以将 foo 移动到 obj.Foo

【讨论】:

    猜你喜欢
    • 2021-06-02
    • 2011-11-04
    • 2021-11-17
    • 1970-01-01
    • 2022-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多