我想写一个带out参数的async方法,像这样:

public async void Method1()
{
    int op;
    int result = await GetDataTaskAsync(out op);
}

我如何做到这一点在GetDataTaskAsync?


当前回答

c# 7+的解决方案是使用隐式元组语法。

    private async Task<(bool IsSuccess, IActionResult Result)> TryLogin(OpenIdConnectRequest request)
    { 
        return (true, BadRequest(new OpenIdErrorResponse
        {
            Error = OpenIdConnectConstants.Errors.AccessDenied,
            ErrorDescription = "Access token provided is not valid."
        }));
    }

返回结果使用方法签名定义的属性名。例句:

var foo = await TryLogin(request);
if (foo.IsSuccess)
     return foo.Result;

其他回答

你不能有带ref或out参数的异步方法。

Lucian Wischik解释了为什么这是不可能的MSDN线程:http://social.msdn.microsoft.com/Forums/en-US/d2f48a52-e35a-4948-844d-828a1a6deb74/why-async-methods-cannot-have-ref-or-out-parameters

As for why async methods don't support out-by-reference parameters? (or ref parameters?) That's a limitation of the CLR. We chose to implement async methods in a similar way to iterator methods -- i.e. through the compiler transforming the method into a state-machine-object. The CLR has no safe way to store the address of an "out parameter" or "reference parameter" as a field of an object. The only way to have supported out-by-reference parameters would be if the async feature were done by a low-level CLR rewrite instead of a compiler-rewrite. We examined that approach, and it had a lot going for it, but it would ultimately have been so costly that it'd never have happened.

对于这种情况,一个典型的解决方法是让async方法返回一个元组。 你可以像这样重写你的方法:

public async Task Method1()
{
    var tuple = await GetDataTaskAsync();
    int op = tuple.Item1;
    int result = tuple.Item2;
}

public async Task<Tuple<int, int>> GetDataTaskAsync()
{
    //...
    return new Tuple<int, int>(1, 2);
}

在异步方法中不能有ref或out参数(如前所述)。

这需要在数据移动中进行一些建模:

public class Data
{
    public int Op {get; set;}
    public int Result {get; set;}
}

public async void Method1()
{
    Data data = await GetDataTaskAsync();
    // use data.Op and data.Result from here on
}

public async Task<Data> GetDataTaskAsync()
{
    var returnValue = new Data();
    // Fill up returnValue
    return returnValue;
}

您获得了更容易重用代码的能力,而且它比变量或元组更具可读性。

我认为像这样使用ValueTuples是可行的。你必须先添加ValueTuple NuGet包:

public async void Method1()
{
    (int op, int result) tuple = await GetDataTaskAsync();
    int op = tuple.op;
    int result = tuple.result;
}

public async Task<(int op, int result)> GetDataTaskAsync()
{
    int x = 5;
    int y = 10;
    return (op: x, result: y):
}

我有同样的问题,因为我喜欢使用Try-method-pattern,基本上似乎与async- wait-paradigm不兼容…

对我来说重要的是,我可以在一个if-子句中调用try -方法,而不必预先定义out-变量,但可以像下面的例子那样内联执行:

if (TryReceive(out string msg))
{
    // use msg
}

所以我提出了以下解决方案:

注意:新的解决方案更优越,因为它可以与在这里的许多其他答案中描述的仅返回元组的方法一起使用,这可能经常在现有代码中找到!

新的解决方案:

Create extension methods for ValueTuples: public static class TupleExtensions { public static bool TryOut<P2>(this ValueTuple<bool, P2> tuple, out P2 p2) { bool p1; (p1, p2) = tuple; return p1; } public static bool TryOut<P2, P3>(this ValueTuple<bool, P2, P3> tuple, out P2 p2, out P3 p3) { bool p1; (p1, p2, p3) = tuple; return p1; } // continue to support larger tuples... } Define async Try-method like this: public async Task<(bool, string)> TryReceiveAsync() { string message; bool success; // ... return (success, message); } Call the async Try-method like this: if ((await TryReceiveAsync()).TryOut(out string msg)) { // use msg }


旧的解决方案:

Define a helper struct: public struct AsyncOut<T, OUT> { private readonly T returnValue; private readonly OUT result; public AsyncOut(T returnValue, OUT result) { this.returnValue = returnValue; this.result = result; } public T Out(out OUT result) { result = this.result; return returnValue; } public T ReturnValue => returnValue; public static implicit operator AsyncOut<T, OUT>((T returnValue ,OUT result) tuple) => new AsyncOut<T, OUT>(tuple.returnValue, tuple.result); } Define async Try-method like this: public async Task<AsyncOut<bool, string>> TryReceiveAsync() { string message; bool success; // ... return (success, message); } Call the async Try-method like this: if ((await TryReceiveAsync()).Out(out string msg)) { // use msg }

对于多个out参数,您可以定义额外的结构(例如AsyncOut<T,OUT1, OUT2>),或者您可以返回一个元组。

c# 7+的解决方案是使用隐式元组语法。

    private async Task<(bool IsSuccess, IActionResult Result)> TryLogin(OpenIdConnectRequest request)
    { 
        return (true, BadRequest(new OpenIdErrorResponse
        {
            Error = OpenIdConnectConstants.Errors.AccessDenied,
            ErrorDescription = "Access token provided is not valid."
        }));
    }

返回结果使用方法签名定义的属性名。例句:

var foo = await TryLogin(request);
if (foo.IsSuccess)
     return foo.Result;