【问题标题】:async lambda expression with arguments带参数的异步 lambda 表达式
【发布时间】:2015-04-11 03:56:45
【问题描述】:

我正在尝试从包装器中调用我的异步函数。但是我收到了这个编译器错误。在下面的代码中,我想获得string x 值,其中大部分等待被抽象/推入CallAsyncFunc 代码。在 c# 4.5 中执行此操作的正确方法是什么?谢谢!

错误 CS4010:无法将异步 lambda 表达式转换为委托类型 'System.Func<int,string>'。异步 lambda 表达式可能返回 voidTaskTask<T>,它们都不能转换为 'System.Func<int,string>'。

public async Task<T2> CallAsyncFunc<T1,T2>(T1 a, Func<T1, T2> func)
{
    return func.Invoke(a);
}

public async Task<string> GetString(int value)
{
    await Task.Run(() => Thread.Sleep(2000));
    return "" + value;
}

public async Task MainAsyncEntry()
{
    string x = await CallAsyncFunc<int, string>(30, async(x) => await GetString(x));
}

【问题讨论】:

  • C# 不是 JavaScript。 "" + value 应该是 value.ToString()

标签: c# async-await


【解决方案1】:

在:

string x = await CallAsyncFunc<int, string>(30, async(x) => await GetString(x));

您有两个x 即第一个问题。试试:

string y = await CallAsyncFunc<int, string>(30, async(x) => await GetString(x));

这里 lambda 的输出是 string,但因为您使用的是 async,所以它应该是 TaskTask&lt;T&gt;

你可以写:

string y = await CallAsyncFunc<int, string>(30, x => GetString(x).Result);
// Equivalent to
string y = await CallAsyncFunc(30, x => GetString(x).Result);

或者

string y = await CallAsyncFunc<int, Task<string>>(30, x => GetString(x)).Result;
// Equivalent to
string y = await CallAsyncFunc(30, GetString).Result;

但无论哪种情况,您的CallAsyncFunc 都会同步运行,因为它不包含任何await

这里的好方法是:

// This
public async Task<TOut> CallAsyncFunc<TIn, TOut>(TIn input, Func<TIn, Task<TOut>> func)
{
    return await func.Invoke(input);
}

// Or those
public Task<TOut> CallAsyncFunc<TIn, TOut>(TIn input, Func<TIn, Task<TOut>> func)
{
    return func.Invoke(input);
}

public async Task<string> GetString(int value)
{
    await Task.Run(() => Thread.Sleep(2000));
    return value.ToString(CultureInfo.InvariantCulture);
}

public async Task MainAsyncEntry()
{
    string y = await CallAsyncFunc(30, GetString);
}

【讨论】:

    【解决方案2】:

    备注1: await GetString(x)Task&lt;string&gt; 解包到string,然后CallAsyncFunc 再次打包到Task&lt;string&gt;您应该将包装后的表达式一直保留在调用堆栈中。据我所知,这是推荐的方式。

    备注2: Resharper 对 CallAsyncFunc 发出警告:

    此异步方法缺少“等待”运算符,将同步运行。考虑使用“await”运算符来等待非阻塞 API 调用,或“await TaskEx.Run(...)”在后台线程上执行 CPU 密集型工作

    实际上,您可以省略async关键字,避免重新包装成Task&lt;string&gt;

    作为备注 1 和备注 2 的结果,您可以编写如下colde:

    // REALLY ASYNC METHOD, WRAPS THE RESULT INTO Task<string>
    public async Task<string> GetString(int value)
    {
        await Task.Run(() => Thread.Sleep(2000));
        return "" + value;
    }
    
    // NOT ASYNC ANY MORE: DOES NOT WRAP THE RESULT INTO Task<T2> any more
    public T2 CallAsyncFunc<T1, T2>(T1 a, Func<T1, T2> func)
    {
        return func.Invoke(a);
    }
    
    // WAIT FOR THE ASYNC RESULT ONLY IN THE OUTER SCOPE (UNWRAPS Task<string> BY THE WAY)
    string y = await CallAsyncFunc(30, GetString);
    string z = await CallAsyncFunc<int, Task<string>>(30, GetString); 
    

    【讨论】:

    • 太棒了!这样可行。反正有没有减少等待获得最终价值?目前,它是'string yval = await await CallAsyncFunc(..)'
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-22
    • 2019-02-22
    • 1970-01-01
    相关资源
    最近更新 更多