带参数的异步lambda表达式

时间:2015-04-10 07:21:12

标签: c# async-await

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

  

错误CS4010:无法将async lambda表达式转换为委托类型    'System.Func<int,string>'。异步lambda表达式可能返回void,    TaskTask<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));
}

2 个答案:

答案 0 :(得分:6)

在:

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<T>

你可以写:

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);
}

答案 1 :(得分:3)

备注1: await GetString(x)Task<string>展开到string,然后将CallAsyncFunc再次换入Task<string>你应该把被包装的表达式保持在你的调用堆栈中。据我所知,这是推荐的方式。

备注2: Resharper警告CallAsyncFunc:

  

这种异步方法缺少'await'运算符并将同步运行。考虑使用'await'运算符等待非阻塞API调用,或'await TaskEx.Run(...)'在后台线程上执行CPU绑定工作

确实,您可以省略async关键字,避免重新包装到Task<string>

作为备注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);