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

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

我如何做到这一点在GetDataTaskAsync?


当前回答

模式匹配来拯救!c# 9(我认为)之后:

// example of a method that would traditionally would use an out parameter
public async Task<(bool success, int? value)> TryGetAsync()
{
    int? value = // get it from somewhere
    
    return (value.HasValue, value);
}

像这样使用它:

if (await TryGetAsync() is (true, int value))
{
    Console.WriteLine($"This is the value: {value}");
}

其他回答

这与Michael Gehling提供的答案非常相似,但我有自己的解决方案,直到我找到了他的解决方案,并注意到我不是第一个想到使用隐式转换的人。

无论如何,当nullable设置为启用时,我也想共享

public readonly struct TryResult<TOut>
{
    #region constructors

    public TryResult(bool success, TOut? value) => (Success, Value) = (success, value);

    #endregion

    #region properties

    public                                            bool  Success { get; init; }
    [MemberNotNullWhen(true, nameof(Success))] public TOut? Value   { get; init; }

    #endregion

    #region methods

    public static implicit operator bool(TryResult<TOut> result) => result.Success;
    public static implicit operator TryResult<TOut>(TOut value) => new (true, value);

    public void Deconstruct(out bool success, out TOut? value) => (success, value) = (Success, Value);

    public TryResult<TOut> Out([NotNullWhen(true)] out TOut? value)
    {
        value = Value;

        return this;
    }

    #endregion
}

然后你可以像这样写一个Try方法:

public static async Task<TryResult<byte[]>> TryGetBytesAsync(string file) =>
    File.Exists(file)
        ? await File.ReadAllBytesAsync(file)
        : default(TryResult<byte[]>);

像这样叫它:

if ((await TryGetBytesAsync(file)).Out(out var bytes))
    Console.WriteLine($"File has {bytes.Length} bytes.");

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参数(如前所述)。

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

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

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

对于真正想要保持参数的开发人员,这里可能有另一种解决方法。

将参数更改为数组或List以封装实际值。记得在发送到方法之前初始化列表。返回后,在使用它之前一定要检查值是否存在。小心编码。

模式匹配来拯救!c# 9(我认为)之后:

// example of a method that would traditionally would use an out parameter
public async Task<(bool success, int? value)> TryGetAsync()
{
    int? value = // get it from somewhere
    
    return (value.HasValue, value);
}

像这样使用它:

if (await TryGetAsync() is (true, int value))
{
    Console.WriteLine($"This is the value: {value}");
}