简体   繁体   中英

How to properly implement DependencyService where one platform has synchronous code and other has asynchronous

I have Xamarin.Forms project implementing DependencyService with platform specific code for Android, iOS and UWP. I created interface for DependencyService with couple asynchronous methods. For some platforms platform specific code is asynchronous and for others synchronous.

Let's say I have an interface in Xamarin.Forms project:

public interface IMyInterface
{
    Task FooAsync();
}

Android implementation of IMyInteface:

public class MyAndroidImplementation : IMyInterface
{
    public async Task FooAsync()
    {
        await DoSomethingAsync();
    }
}

iOS implementation of IMyInterface:

public class MyiOSImplementation : IMyInterface
{
    public async Task FooAsync()
    {
        DoSomething();
    }
}

There is only synchronous code in iOS implementation. This raises warning with message:

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

What is the proper way of dealing with this type of scenario?

I don't like wrapping synchronous code to asynchronous 'await Task.Run()' since this is clearly a lie that there is an asynchronous code. Would it be better to design IMyInterface with void method and then in platform specific implementation run the code on 'Task.Run()'?

I recommend ignoring the warning with a #pragma . The warning is there to inform you that you probably forgot an await , but since you know the code is synchronous, you can safely ignore the warning.

If you have to do this several times, you can use a helper method like this to only ignore the warning in a single place. Usage:

public Task FooAsync() => TaskHelper.ExecuteAsTask(() =>
{
    DoSomething();
});

If you do use Task.FromResult or Task.CompletedTask , you should also wrap your synchronous code in a try / catch and return Task.FromException if there is an exception. It's expected that Task -returning methods will place exceptions on the returned task rather than throw them directly.

I think you'll have to keep both asynchronous, since one of them needs it. Here's how you can make it cleaner.

By returning Task.CompletedTask you make it seem like there was some async work done and it has completed, thus making it 'cleaner'.

If you're interested in why returning Task.CompletedTask is a good idea, check out this post

public class MyiOSImplementation : IMyInterface
{
    public Task FooAsync()
    {
        DoSomething();
        return Task.CompletedTask;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM