如何用out参数编写asynchronous方法?

我想写一个out参数的asynchronous方法,如下所示:

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

如何在GetDataTaskAsync执行此操作?

你不能有refout参数的asynchronous方法。

Lucian Wischik解释了为什么在这个MSDN线程上这是不可能的: http ://social.msdn.microsoft.com/Forums/en-US/d2f48a52-e35a-4948-844d-828a1a6deb74/why-async-methods-cannot-have -ref或出局参数

至于为什么asynchronous方法不支持out-by-reference参数? (或参考?)这是CLR的限制。 我们select以类似于迭代器方法的方式实现asynchronous方法 – 即通过编译器将方法转换为状态机对象。 CLR没有安全的方法来将“输出参数”或“参考参数”的地址存储为对象的字段。 如果asynchronousfunction是通过低级CLR重写而不是编译器重写完成的,那么支持out-by-reference参数的唯一方法是。 我们仔细研究了这个方法,这个方法有很多的工作要做,但最终会花费这么多,以至于根本就不会发生。

这种情况的典型解决方法是让asynchronous方法返回一个Tuple。 你可以重新编写你的方法:

 public async void 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): } 

async方法中不能有refout参数(正如已经提到的那样)。

这在数据移动的一些build模尖叫:

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

你可以更容易地重用你的代码,加上它比variables或元组更易读。

亚历克斯在可读性方面做了很多工作。 等价地,一个函数也是足够的接口来定义返回的types,你也可以得到有意义的variables名。

 delegate void OpDelegate(int op); Task<bool> GetDataTaskAsync(OpDelegate callback) { bool canGetData = true; if (canGetData) callback(5); return Task.FromResult(canGetData); } 

调用者提供一个lambda(或一个命名的函数),intellisense通过从委托中复制variables名来帮助。

 int myOp; bool result = await GetDataTaskAsync(op => myOp = op); 

这种特殊的方法就像一个“Try”方法,如果方法结果为truemyOp被设置。 否则,你不关心myOp

out参数的一个很好的特性是即使函数抛出一个exception,它们也可以用来返回数据。 我认为与async方法做这个最接近的相当于使用一个新的对象来保存async方法和调用者可以引用的数据。 另一种方法是按照另一个答案中的build议传递一个委托 。

请注意,这两种技术都不会有编译器的强制执行。 也就是说,编译器不会要求您在共享对象上设置值或调用在委托中传递的值。

下面是一个使用共享对象模拟refout的示例实现out用于async方法和refout不可用的其他各种场景:

 class Ref<T> { // Field rather than a property to support passing to functions // accepting `ref T` or `out T`. public T Value; } async Task OperationExampleAsync(Ref<int> successfulLoopsRef) { var things = new[] { 0, 1, 2, }; var i = 0; while (true) { // Fourth iteration will throw an exception, but we will still have // communicated data back to the caller via successfulLoopsRef. things[i] += i; successfulLoopsRef.Value++; i++; } } async Task UsageExample() { var successCounterRef = new Ref<int>(); // Note that it does not make sense to access successCounterRef // until OperationExampleAsync completes (either fails or succeeds) // because there's no synchronization. Here, I think of passing // the variable as “temporarily giving ownership” of the referenced // object to OperationExampleAsync. Deciding on conventions is up to // you and belongs in documentation ^^. try { await OperationExampleAsync(successCounterRef); } finally { Console.WriteLine($"Had {successCounterRef.Value} successful loops."); } }