153 votes

Rendre les implémentations d'interface asynchrones

J'essaie actuellement de réaliser mon application en utilisant des méthodes asynchrones. Toutes mes entrées-sorties sont effectuées par des implémentations explicites d'une interface et je suis un peu perdu sur la façon de rendre les opérations asynchrones.

Comme je vois les choses, j'ai deux options dans la mise en œuvre :

interface IIO
{
    void DoOperation();
}

OPTION1 : Faire une implémentation implicite asynchrone et attendre le résultat dans l'implémentation implicite.

class IOImplementation : IIO
{

     async void DoOperation()
    {
        await Task.Factory.StartNew(() =>
            {
                //WRITING A FILE OR SOME SUCH THINGAMAGIG
            });
    }

    #region IIO Members

    void IIO.DoOperation()
    {
        DoOperation();
    }

    #endregion
}

OPTION2 : Effectuer l'implémentation explicite de manière asynchrone et attendre la tâche de l'implémentation implicite.

class IOAsyncImplementation : IIO
{
    private Task DoOperationAsync()
    {
        return new Task(() =>
            {
                //DO ALL THE HEAVY LIFTING!!!
            });
    }

    #region IIOAsync Members

    async void IIO.DoOperation()
    {
        await DoOperationAsync();
    }

    #endregion
}

L'une de ces mises en œuvre est-elle meilleure que l'autre ou existe-t-il une autre solution à laquelle je ne pense pas ?

291voto

svick Points 81772

Aucune de ces options n'est correcte. Vous essayez d'implémenter une interface synchrone de manière asynchrone. Ne le faites pas. Le problème est que lorsque DoOperation() revient, l'opération n'est pas encore terminée. Pire encore, si une exception se produit pendant l'opération (ce qui est très courant avec les opérations d'E/S), l'utilisateur n'aura pas la possibilité de traiter cette exception.

Ce que vous devez faire, c'est modifier l'interface de sorte qu'il est asynchrone :

interface IIO
{
    Task DoOperationAsync(); // note: no async here
}

class IOImplementation : IIO
{
    public async Task DoOperationAsync()
    {
        // perform the operation here
    }
}

De cette façon, l'utilisateur verra que l'opération est async et ils seront en mesure de await il. Cela oblige également les utilisateurs de votre code à passer à l'option async mais c'est inévitable.

De plus, je suppose qu'en utilisant StartNew() dans votre implémentation est juste un exemple, vous ne devriez pas en avoir besoin pour implémenter l'IO asynchrone. (Et new Task() c'est encore pire, ça ne marchera pas, parce que tu n'as pas Start() le site Task .)

29voto

Dima Points 694

La meilleure solution consiste à introduire une autre interface pour les opérations asynchrones. La nouvelle interface doit hériter de l'interface originale.

Exemple :

interface IIO
{
    void DoOperation();
}

interface IIOAsync : IIO
{
    Task DoOperationAsync();
}

class ClsAsync : IIOAsync
{
    public void DoOperation()
    {
        DoOperationAsync().GetAwaiter().GetResult();
    }

    public async Task DoOperationAsync()
    {
        //just an async code demo
        await Task.Delay(1000);
    }
}

class Program
{
    static void Main(string[] args)
    {
        IIOAsync asAsync = new ClsAsync();
        IIO asSync = asAsync;

        Console.WriteLine(DateTime.Now.Second);

        asAsync.DoOperation();
        Console.WriteLine("After call to sync func using Async iface: {0}", 
            DateTime.Now.Second);

        asAsync.DoOperationAsync().GetAwaiter().GetResult();
        Console.WriteLine("After call to async func using Async iface: {0}", 
            DateTime.Now.Second);

        asSync.DoOperation();
        Console.WriteLine("After call to sync func using Sync iface: {0}", 
            DateTime.Now.Second);

        Console.ReadKey(true);
    }
}

P.S. Redéfinissez vos opérations asynchrones pour qu'elles renvoient Task au lieu de void, sauf si vous devez vraiment renvoyer void.

1voto

datchung Points 1586

J'ai créé un exemple d'application basé sur la réponse de Svick et j'ai constaté qu'appeler IOImplementation.DoOperationAsync() sans le async ne donne pas lieu à un avertissement du compilateur ou de Visual Studio. Ce résultat est basé sur Visual Studio 2019 et .NET Core 3.1.

Exemple de code ci-dessous.

public interface ISomething
{
    Task DoSomethingAsync();
}

public class Something : ISomething
{
    public async Task DoSomethingAsync()
    {
        await Task.Run(() => Thread.Sleep(2000));
        Console.WriteLine("Message from DoSomethingAsync");

        throw new Exception("Some exception");
    }
}

class Program
{
    static void Main(string[] args)
    {
        ISomething something = new Something();

        Console.WriteLine("pre something.DoSomethingAsync() without await");
        something.DoSomethingAsync(); // No compiler warning for missing "await" and exception is "swallowed"
        Console.WriteLine("post something.DoSomethingAsync() without await");

        Thread.Sleep(3000);

        // Output:
        // pre something.DoSomethingAsync() without await
        // post something.DoSomethingAsync() without await
        // Message from DoSomethingAsync
    }
}

-4voto

Alatey Points 21

Une classe abstraite peut être utilisée à la place d'une interface (en C# 7.3).

// Like interface
abstract class IIO
{
    public virtual async Task<string> DoOperation(string Name)
    {
        throw new NotImplementedException(); // throwing exception
        // return await Task.Run(() => { return ""; }); // or empty do
    }
}

// Implementation
class IOImplementation : IIO
{
    public override async Task<string> DoOperation(string Name)
    {
        return await await Task.Run(() =>
        {
            if(Name == "Spiderman")
                return "ok";
            return "cancel";
        }); 
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X