6

I have an interface, which delivers me a certain path. In one of my implementations I need to use async, but I haven't figured out how to get the result of an async method into a synchronous method. Here is the code sample:

Interface:

public interface IFilePath
{
    string GetAsset();
}

Problematic implementation:

public class FilePath : IFilePath
{
    public string GetAsset()
    {
        return GetAssetAssync();
    }

    private async Task<string> GetAssetAssync()
    {
        StorageFolder assetsFolder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync(@"Assets").AsTask().ConfigureAwait(false);
        return assetsFolder.Path;
    }
}

Only for this implementation I need the async call. All others do not need it. So I can't use public Task<string> GetAsset() or can I somehow?

testing
  • 19,681
  • 50
  • 236
  • 417
  • 2
    What's your question? Either add both signatures in the interface, or create a separate async-only library. [Certainly don't call `...Async().Result` from synchronous code](http://stackoverflow.com/questions/22628087/calling-async-method-synchronously) (deadlocks, exception handling). – CodeCaster Mar 16 '16 at 13:37
  • `.Result`. However, in general, you're better off only offering the async version and leave it up to the callers to call `.Result` themselves if they insist on a synchronous version, rather than wrapping it yourself. This encourages other callers to use the async version as well, instead of taking the easy way out and writing (inefficiently wrapped) blocking code. – Jeroen Mostert Mar 16 '16 at 13:37
  • Or you could simply do `GetAssetAssync().ConfigureAwait(false);` which will not deadlock – wingerse Mar 16 '16 at 13:45
  • @CodeCaster: When calling the interface method I don't know which signature I need. The interface should have the responsibility to pass the call to the correct implementation. I only want a path. What do I have to change for that? If I want to use both signatures I would have to use preprocessor directives. But then would it make sense to use an interface at all? What do you mean with async-only library exactly? I guess it will result in the same that I don't use the interface anymore. – testing Mar 16 '16 at 13:46
  • @JeroenMostert: Only one of my implementations needs the async part. So I should use `async Task GetAssetAssync()` for all implementations? – testing Mar 16 '16 at 13:49
  • 1
    @testing but your interface cannot be both synchronous _and_ asynchronous (unless you supply two different methods that every implementation has to implement). So you need to decide if you want the interface to be asynchronous and let the client decide whether to call is asynchronously or synchronously, or make it synchronous and call that specific asynchronous implementation synchronously. – D Stanley Mar 16 '16 at 13:54
  • "If I want to use both signatures I would have to use preprocessor directives" I don;t see how that will help - those only are considered when the _interface_ is compiled. When clients reference it they'll only see one signature. You can't use preprocessor directives on the _client_ to choose one signature over another. – D Stanley Mar 16 '16 at 13:56
  • 1
    Considering that your code should apparently run on Windows Mobile, doing everything async that could involve I/O or other lengthy operations seems like it would be a best practice anyway. If your interface has an async method but the caller needs a sync implementation, this is trivial to achieve on the caller end (at the cost of efficiency). The reverse situation (where you only have a sync implementation that then needs to be wrapped to be async) is worse in terms of overhead. Thanks to `async`/`await`, async methods are hardly more difficult to use. – Jeroen Mostert Mar 16 '16 at 13:59
  • @DStanley: You are right. That is the issue. What is the better option of the both? If I make the interface synchronous then I'll block the thread. How would the code look like with an asynchronous interface? "preprocessor directives": The caller could make use of conditional compilation to decide if he would call the synchronous or asynchronous version. – testing Mar 16 '16 at 14:12
  • @testing My point is that preprocessor directives only work when you _build_ the interface. So unless you're compiling the client and the interface at the same time then there's only one interface signature. – D Stanley Mar 16 '16 at 14:14
  • @JeroenMostert: So if I do everything async, how would the code look like? Now I use `Task GetAssetAsync();` for the interface. How do I return Task when I want to return a string? – testing Mar 16 '16 at 14:17
  • 2
    `Task.FromResult("String")`. – Jeroen Mostert Mar 16 '16 at 14:19
  • @DStanley: Your are right. Didn't thought about that, but now I see your point. – testing Mar 16 '16 at 14:19
  • @JeroenMostert: Thanks. One more question about "*If your interface has an async method but the caller needs a sync implementation, this is trivial to achieve on the caller end*": You mean calling `Task.Run(GetAssetAsync).Result`? – testing Mar 16 '16 at 14:29
  • No -- `GetAssetAsync().Result` will synchronously wait for the task returned by `GetAssetAsync` to complete, there is no need to introduce even more new tasks. But be sure to read http://stackoverflow.com/questions/13489065/; if you want to include this in a library and the method should be safe for all callers, including those that may be using UI code, you probably want to use `.ConfigureAwait(false)` to avoid potential deadlocks. Your callers are better off biting the bullet and going async themselves, though. – Jeroen Mostert Mar 16 '16 at 14:43
  • @JeroenMostert: Seems to work now. If you put your comments in an answer, I can accept it. – testing Mar 16 '16 at 15:12
  • Accept Stephen's answer, because he's not lazy like me (and also much sexier when it comes to asynchronous programming). – Jeroen Mostert Mar 16 '16 at 15:57

1 Answers1

7

A Task-returning method indicates that the implementation may be asynchronous. So, the best approach is to update your interface to allow asynchronous implementations:

public interface IFilePath
{
  Task<string> GetAssetAsync();
}

I would attempt to make the other implementations asynchronous (file I/O is a naturally asynchronous operation), but if you have truly synchronous implementations (e.g., reading from an in-memory zip file or something), then you can wrap your result in Task.FromResult:

class SynchronousFilePath: IFilePath
{
  public string GetAsset(); // natural synchronous implementation

  public Task<string> GetAssetAsync()
  {
    return Task.FromResult(GetAsset());
  }
}
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • 1
    As an aside to this answer: If using the Task.FromResult in the implementation I would document that it will block the calling thread for your dear desktop application devs who will expect to be able to await this and keep their UI thread unblocked only to discover that they're being blocked from the implementation detail of .FromResult. – davidallyoung Mar 17 '16 at 13:50