Async interface, sync-only implementation: Which of `Task.FromResult` or async-over-sync is the lesser evil?
07:22 01 Apr 2026

I have this interface to access files from various providers:

interface IFileProvider
{
    Task FileExistsAsync(string filePath);
    ...
}

Some of its implementations use network requests supporting asynchronous calls, but one does not: The basic filesystem implementation, using File.Exists().

Performance-wise or deadlock-risk-wise, which is the lesser evil, between just doing the blocking call on the calling thread:

class FileSystemImplementation : IFileProvider
{
    Task FileExistsAsync(string filePath) => Task.FromResult(File.Exists(filePath));
}

or doing async-over-sync?

class FileSystemImplementation : IFileProvider
{
    Task FileExistsAsync(string filePath) => Task.Run(() => File.Exists(filePath));
}

or even

class FileSystemImplementation : IFileProvider
{
    async Task FileExistsAsync(string filePath) => await Task.Run(() => File.Exists(filePath));
}

?

c# asynchronous