Como criar um método aguardável corretamente

Estou escrevendo alguns métodos aguardáveis e encontrei muitas maneiras de fazê-lo na internet. Então eu vim aqui para saber o que realmente está acontecendo em cada sentido, e se alguns modos devem ser banidos.

Até onde eu sei, existem dois tipos de métodos aguardáveis:

Aqueles que chamam outros métodos aguardáveis:
public async Task<Foo> GetFooAsync()
{
    var foo = await TrulyGetFooAsync();

    // Do stuff with foo

    return foo;
}

Não encontrei outra maneira de fazer isso e acho que é o caminho certo. Diga-me se estou errado!

Aqueles que chamam apenas métodos não aguardáveis:

E aqui os problemas vêm à minha mente.

Por exemplo, eu vi isso:

Exemplo 1
public async Task<Foo> GetFooAsync()
{
    return await Task.Run(() => TrulyGetFoo());
}

Tanto quanto eu entendo, as palavras-chave async / waitit são inúteis e podem ser evitadas para fornecer isso:

Exemplo 2
public Task<Foo> GetFooAsync()
{
    return Task.Run(() => TrulyGetFoo());
}

Este último exemplo é o que eu estava fazendo até agora. Sobre isso, existe uma diferença entre:

Task.Run(() => TrulyGetFoo());

e

Task.Run((Foo)TrulyGetFoo); // I don't know if the cast is required at any time but in my code, it was

???

Mas recentemente encontrei o seguinte:

Exemplo 3
public Task<Foo> GetFooAsync()
{
    TaskCompletionSource<Foo> tcs = new TaskCompletionSource<Foo>();
    tcs.SetResult(TrulyGetFoo());
    return tcs.Task;
}

Se eu entendi corretamente, um método aguardável nem sempre é executado em outro thread ??? Meu palpite é que o terceiro exemplo está fornecendo esse mecanismo (mas como ??? só vejo código síncrono no terceiro exemplo), quando os exemplos 1 e 2 sempre serão executados em um thread de trabalho ???

Pode haver ainda outras maneiras de escrever métodos aguardáveis, então, deixe-me saber sobre eles.

questionAnswers(3)

yourAnswerToTheQuestion