Это анти-паттерн. Если вы делаете библиотеку, синхронные методы должны быть строго синхронными, а асинхронные должны быть строго асинхронными.
Если разработчик вызывает синхронный метод, он ожидает, что выполнение не будет занимать потоки в тред-пуле (если суть метода не отображает явной необходимости такого поведения). Если вы сделали синхронный враппер, внутренние таски могут занимать потоки в тред пуле, и пользователь библиотеки может очень долго ловить ошибку. Кроме этого во время отладки намного удобней видеть синхронный call stack, нежели асинхронный.
Еще одна проблема. Например ваш класс/метод принимает как аргумент любую реализацию Stream, и разработчик ожидает, что синхронная версия метода будет вызывать синхронные методы на экземпляре Stream: Read/Write/Flush, а асинхронная будет работать с ReadAsync/WriteAsync/FlushAsync. Это логичное поведение, и не всегда пользователям библиотеки будут нужны дополнительные расходы на создания объектов Task, если они пользуются только MemoryStream.
При таком подходе будет дублироваться код, но с этим ничего не сделать.
Подробнее об этом: https://blogs.msdn.microsoft.com/pfxteam/2012/04/13/should-i-expose-synchronous-wrappers-for-asynchronous-methods/
В статье так же отмечено, что изначально метод HttpWebRequest.GetResponse работал через асинхронные методы, но потом это исправили, так как создавались некоторые проблемы.
Схожий вопрос на англоязычным stackoverflow: https://stackoverflow.com/questions/54261169/async-and-sync-versions-of-method
Пример оттуда же для уменьшения дублирования кода:
private static async Task DownloadToCacheAsync(bool sync)
{
...do some analysis to get download locations...
using (var wc = new WebClient())
{
if (sync)
wc.DownloadFile(new Uri(content.Url), targetPath);
else
await wc.DownloadFileTaskAsync(new Uri(content.Url), targetPath);
}
...do other stuff...
}
public static Task DownloadToCacheAsync() => DownloadToTaskAsync(sync: false);
public static void DownloadToCache() => DownloadToTaskAsync(sync: true).GetAwaiter().GetResult();