You can't have async methods with ref or out parameters.
C# Asynchronous programming: Is it possible to have async methods with ref or out parameters? Unfortunately, because of CLR limitations, we can not do that!
Unfortunately in internal state class we can't store the address of an out or ref parameter, basically CLR has no safe way to deal with the address of an object. As consequence the compiler forbid the use of Ref and out parameters in asynchronous method.
However, just to address "Call an async method in C# without await", you can execute the async method inside a Task. Run . This approach will wait until MyAsyncMethod finish. await asynchronously unwraps the Result of your task, whereas just using Result would block until the task had completed.
You can't have async methods with ref
or out
parameters.
Lucian Wischik explains why this is not possible on this MSDN thread: 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.
A typical workaround for this situation is to have the async method return a Tuple instead. You could re-write your method as such:
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);
}
You cannot have ref
or out
parameters in async
methods (as was already noted).
This screams for some modelling in the data moving around:
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;
}
You gain the ability to reuse your code more easily, plus it's way more readable than variables or tuples.
The C#7+ Solution is to use implicit tuple syntax.
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."
}));
}
return result utilizes the method signature defined property names. e.g:
var foo = await TryLogin(request);
if (foo.IsSuccess)
return foo.Result;
I had the same problem as I like using the Try-method-pattern which basically seems to be incompatible to the async-await-paradigm...
Important to me is that I can call the Try-method within a single if-clause and do not have to pre-define the out-variables before, but can do it in-line like in the following example:
if (TryReceive(out string msg))
{
// use msg
}
So I came up with the following solution:
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
}
For multiple out parameters you can define additional structs (e.g. AsyncOut<T,OUT1, OUT2>) or you can return a tuple.
Alex made a great point on readability. Equivalently, a function is also interface enough to define the type(s) being returned and you also get meaningful variable names.
delegate void OpDelegate(int op);
Task<bool> GetDataTaskAsync(OpDelegate callback)
{
bool canGetData = true;
if (canGetData) callback(5);
return Task.FromResult(canGetData);
}
Callers provide a lambda (or a named function) and intellisense helps by copying the variable name(s) from the delegate.
int myOp;
bool result = await GetDataTaskAsync(op => myOp = op);
This particular approach is like a "Try" method where myOp
is set if the method result is true
. Otherwise, you don't care about myOp
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With