3

This async method generates two CS0165 errors - Use of unassigned local variable.

public async Task<Tuple<bool, string>> DoWorkAsync(object[] objs)
{
    string msg;
    bool result;
    await Task.Run(() => result = _foo.DoWork(objs, out msg));
    return new Tuple<bool, string>(result, msg);
}

Initially I was going to ask why, but now I'm pretty sure it's because an exception may be raised within the local Task.

See magician Jon Skeet's comment below: the compiler doesn't know what Task.Run is doing, and cannot infer that the locals will be assigned.

So now I'll ask: what's the best way to implement this? Assign default values to the locals, create a local for the task and check task.IsFaulted before returning?

Should I generally propagate the exception? I suppose that's probably an unanswerable question depending on the context.

Michael
  • 1,803
  • 1
  • 17
  • 26
  • 3
    For starters, don't use `Task.Run` to fake async. – i3arnon Dec 07 '15 at 17:17
  • @i3arnon What do you mean 'fake' async? How do you implement a method at the bottom of an async chain? Maybe you have something beyond "that's not the right way" to add? – Michael Dec 07 '15 at 17:21
  • 1
    This is how: http://stackoverflow.com/a/24539523/885318 – i3arnon Dec 07 '15 at 17:23
  • 3
    It's not so much because an exception may be raised, as because the C# compiler doesn't know anything about what `Task.Run` does. That's just an arbitrary method as far as it's concerned. – Jon Skeet Dec 07 '15 at 17:23
  • 2
    And this explains why you shouldn't expose asynchronous wrappers for synchronous methods: http://blogs.msdn.com/b/pfxteam/archive/2012/03/24/10287244.aspx – i3arnon Dec 07 '15 at 17:24
  • @i3arnon Thanks. Makes sense. I've learned these same various async/await constructs a dozen times. – Michael Dec 07 '15 at 17:41
  • @JonSkeet I can't stop thinking about this point. Can the compiler not infer from the context of the anon method what will happen? Or perhaps it is a matter of the fact there is no contract saying that Task.Run is going to call that method? – Michael Dec 07 '15 at 17:53
  • 1
    @Michael: No, it can't. Imagine you were calling `Foo.Bar` there instead of `Task.Run` - do you think that should change things? Imagine that `Foo.Bar` was just a method returning a `Task`, and never executed the delegate. – Jon Skeet Dec 07 '15 at 18:02
  • @i3arnon Wanted to say thanks again. Refactored the project I was working on and it is significantly cleaner and makes a lot more sense now. Problems looking like nails, and all that. – Michael Dec 07 '15 at 18:42
  • @Michael sure, anytime :) – i3arnon Dec 07 '15 at 18:42

1 Answers1

3

Return the Tuple from your Task.Run, like so:

public Task<Tuple<bool, string>> DoWorkAsync(object[] objs)
{
    return Task.Run(() => 
    {
        string msg;
        var result = _foo.DoWork(objs, out msg);
        return new Tuple<bool, string>(result, msg);
    });
}

Now you don't need to worry about uninitialized local variables.

Lukazoid
  • 19,016
  • 3
  • 62
  • 85
  • Oh. Duh. That seems much cleaner. – Michael Dec 07 '15 at 17:18
  • But with this solution your method is no longer `async`. (Though you may not need it since you don't `await` anymore). – René Vogt Dec 07 '15 at 17:25
  • @RenéVogt it doesn't need to be to begin with. But it can still use async and await instead of a simple return. – i3arnon Dec 07 '15 at 17:26
  • 1
    @RenéVogt The `async` keyword has become redundant as we can just return the `Task` from `Task.Run` directly. If you wish you can still put the `async`/`await` keywords in, but you don't really gain and just lose in performance slightly. – Lukazoid Dec 07 '15 at 17:27
  • Got it, and after updating my answer I realized it's almost exactly yours, so I deleted mine. – René Vogt Dec 07 '15 at 17:36
  • Thanks for the edification Lukazoid and @I3amon. I understand why your answer is preferable to mine although mine does work(?). I know understand that by assigning _foo.DoWork to a Task it will run asynchronously. – DaniDev Dec 07 '15 at 19:42