我想写一个带out参数的async方法,像这样:
public async void Method1()
{
int op;
int result = await GetDataTaskAsync(out op);
}
我如何做到这一点在GetDataTaskAsync?
我想写一个带out参数的async方法,像这样:
public async void Method1()
{
int op;
int result = await GetDataTaskAsync(out op);
}
我如何做到这一点在GetDataTaskAsync?
当前回答
我认为像这样使用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):
}
其他回答
我认为像这样使用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):
}
你不能有带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;
}
您获得了更容易重用代码的能力,而且它比变量或元组更具可读性。
这与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.");
对于真正想要保持参数的开发人员,这里可能有另一种解决方法。
将参数更改为数组或List以封装实际值。记得在发送到方法之前初始化列表。返回后,在使用它之前一定要检查值是否存在。小心编码。