121 votes

Suppression de l'alerte CS1998 : Cette méthode asynchrone est dépourvue de 'await'.

J'ai une interface avec des fonctions qui retournent Task . Certaines des classes qui mettent en œuvre l'interface n'ont rien à attendre, tandis que d'autres peuvent simplement lancer - les avertissements sont donc fallacieux et ennuyeux.

Est-il possible de supprimer ces avertissements ? Par exemple :

public async Task<object> test()
{
    throw new NotImplementedException();
}

rendements :

avertissement CS1998 : Cette méthode asynchrone est dépourvue d'opérateurs 'await' et s'exécutera synchrone. Pensez à utiliser l'opérateur 'await' pour attendre des appels d'API non bloquants, ou 'await Task.Run(...)' pour effectuer des travaux liés au CPU sur un thread d'arrière-plan.

124voto

Stephen Cleary Points 91731

J'ai une interface avec des fonctions asynchrones.

Méthodes de retour Task je crois. async est un détail d'implémentation, il ne peut donc pas être appliqué aux méthodes d'interface.

Certaines classes qui implémentent l'interface n'ont rien à attendre, et d'autres peuvent simplement lancer.

Dans ces cas, vous pouvez profiter du fait que async est un détail de mise en œuvre.

Si vous n'avez rien à await alors vous pouvez simplement retourner Task.FromResult :

public Task<int> Success() // note: no "async"
{
  ... // non-awaiting code
  int result = ...;
  return Task.FromResult(result);
}

En cas de jet NotImplementedException la procédure est un peu plus compliquée :

public Task<int> Fail() // note: no "async"
{
  var tcs = new TaskCompletionSource<int>();
  tcs.SetException(new NotImplementedException());
  return tcs.Task;
}

Si vous avez beaucoup de méthodes qui lancent NotImplementedException (ce qui, en soi, peut indiquer qu'une refonte au niveau de la conception serait utile), alors vous pourriez regrouper le manque de mots dans une classe d'aide :

public static class TaskConstants<TResult>
{
  static TaskConstants()
  {
    var tcs = new TaskCompletionSource<TResult>();
    tcs.SetException(new NotImplementedException());
    NotImplemented = tcs.Task;
  }

  public static Task<TResult> NotImplemented { get; private set; }
}

public Task<int> Fail() // note: no "async"
{
  return TaskConstants<int>.NotImplemented;
}

La classe d'aide réduit également la quantité de déchets que le GC devrait autrement collecter, puisque chaque méthode ayant le même type de retour peut partager sa classe d'aide. Task y NotImplementedException objets.

J'ai plusieurs autres exemples de type "constante de tâche" dans ma bibliothèque AsyncEx .

73voto

Jamie Macia Points 131

Une autre option, si vous voulez garder le corps de la fonction simple et ne pas écrire de code pour la prendre en charge, consiste simplement à supprimer l'avertissement avec #pragma :

#pragma warning disable 1998
public async Task<object> Test()
{
    throw new NotImplementedException();
}
#pragma warning restore 1998

Si cela est assez courant, vous pourriez placer la déclaration de désactivation en haut du fichier et omettre la restauration.

http://msdn.microsoft.com/en-us/library/441722ys(v=vs.110).aspx

44voto

zeroskyx Points 71

Une autre façon de préserver le mot-clé async (au cas où vous voudriez le garder) est d'utiliser :

public async Task StartAsync()
{
    await Task.Yield();
}

Une fois que vous avez rempli la méthode, vous pouvez simplement supprimer la déclaration. Je l'utilise souvent, notamment lorsqu'une méthode peut attendre quelque chose mais que toutes les implémentations ne le font pas.

18voto

Roman Pokrovskij Points 1310

Il y a une différence entre les solutions et, à proprement parler, vous devez savoir comment l'appelant va appeler la méthode asynchrone, mais avec le modèle d'utilisation par défaut qui suppose ".Wait()" sur le résultat de la méthode - ". return Task.CompletedTask " est la meilleure solution.

    BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 3 [1709, Fall Creators Update] (10.0.16299.192)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233537 Hz, Resolution=309.2589 ns, Timer=TSC
.NET Core SDK=2.1.2
  [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
  Clr    : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2600.0
  Core   : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT

         Method |  Job | Runtime |         Mean |       Error |      StdDev |       Median |          Min |          Max | Rank |  Gen 0 |  Gen 1 |  Gen 2 | Allocated |
--------------- |----- |-------- |-------------:|------------:|------------:|-------------:|-------------:|-------------:|-----:|-------:|-------:|-------:|----------:|
 CompletedAwait |  Clr |     Clr |    95.253 ns |   0.7491 ns |   0.6641 ns |    95.100 ns |    94.461 ns |    96.557 ns |    7 | 0.0075 |      - |      - |      24 B |
      Completed |  Clr |     Clr |    12.036 ns |   0.0659 ns |   0.0617 ns |    12.026 ns |    11.931 ns |    12.154 ns |    2 | 0.0076 |      - |      - |      24 B |
         Pragma |  Clr |     Clr |    87.868 ns |   0.3923 ns |   0.3670 ns |    87.789 ns |    87.336 ns |    88.683 ns |    6 | 0.0075 |      - |      - |      24 B |
     FromResult |  Clr |     Clr |   107.009 ns |   0.6671 ns |   0.6240 ns |   107.009 ns |   106.204 ns |   108.247 ns |    8 | 0.0584 |      - |      - |     184 B |
          Yield |  Clr |     Clr | 1,766.843 ns |  26.5216 ns |  24.8083 ns | 1,770.383 ns | 1,705.386 ns | 1,800.653 ns |    9 | 0.0877 | 0.0038 | 0.0019 |     320 B |
 CompletedAwait | Core |    Core |    37.201 ns |   0.1961 ns |   0.1739 ns |    37.227 ns |    36.970 ns |    37.559 ns |    4 | 0.0076 |      - |      - |      24 B |
      Completed | Core |    Core |     9.017 ns |   0.0690 ns |   0.0577 ns |     9.010 ns |     8.925 ns |     9.128 ns |    1 | 0.0076 |      - |      - |      24 B |
         Pragma | Core |    Core |    34.118 ns |   0.4576 ns |   0.4281 ns |    34.259 ns |    33.437 ns |    34.792 ns |    3 | 0.0076 |      - |      - |      24 B |
     FromResult | Core |    Core |    46.953 ns |   1.2728 ns |   1.1905 ns |    46.467 ns |    45.674 ns |    49.868 ns |    5 | 0.0533 |      - |      - |     168 B |
          Yield | Core |    Core | 2,480.980 ns | 199.4416 ns | 575.4347 ns | 2,291.978 ns | 1,810.644 ns | 4,085.196 ns |   10 | 0.0916 |      - |      - |     296 B |

Note : FromResult ne peuvent pas être comparées directement.

Code de test :

   [RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
   [ClrJob, CoreJob]
   [HtmlExporter, MarkdownExporter]
   [MemoryDiagnoser]
 public class BenchmarkAsyncNotAwaitInterface
 {
string context = "text context";
[Benchmark]
public int CompletedAwait()
{
    var t = new CompletedAwaitTest();
    var a = t.DoAsync(context);
    a.Wait();
    return t.Length;
}

[Benchmark]
public int Completed()
{
    var t = new CompletedTest();
    var a = t.DoAsync(context);
    a.Wait();
    return t.Length;
}

[Benchmark]
public int Pragma()
{
    var t = new PragmaTest();
    var a = t.DoAsync(context);
    a.Wait();
    return t.Length;
}

[Benchmark]
public int Yield()
{
    var t = new YieldTest();
    var a = t.DoAsync(context);
    a.Wait();
    return t.Length;
}

    [Benchmark]
    public int FromResult()
    {
        var t = new FromResultTest();
        var t2 = t.DoAsync(context);
        return t2.Result;
    }

public interface ITestInterface
{
    int Length { get; }
    Task DoAsync(string context);
}

class CompletedAwaitTest : ITestInterface
{
    public int Length { get; private set; }
    public async Task DoAsync(string context)
    {
        Length = context.Length;
        await Task.CompletedTask;
    }
}

class CompletedTest : ITestInterface
{
    public int Length { get; private set; }
    public Task DoAsync(string context)
    {
        Length = context.Length;
        return Task.CompletedTask;
    }
}

class PragmaTest : ITestInterface
{
    public int Length { get; private set; }
    #pragma warning disable 1998
    public async Task DoAsync(string context)
    {
        Length = context.Length;
        return;
    }
    #pragma warning restore 1998
}

class YieldTest : ITestInterface
{
    public int Length { get; private set; }
    public async Task DoAsync(string context)
    {
        Length = context.Length;
        await Task.Yield();
    }
}

    public interface ITestInterface2
    {
        Task<int> DoAsync(string context);
    }

    class FromResultTest : ITestInterface2
    {
        public async Task<int> DoAsync(string context)
        {
            var i = context.Length;
            return await Task.FromResult(i);
        }
    }

}

10voto

rrreee Points 41

Je sais qu'il s'agit d'un vieux fil de discussion, et peut-être que cela n'aura pas le bon effet pour tous les usages, mais ce qui suit est aussi proche que possible de la possibilité de simplement lancer une NotImplementedException lorsque je n'ai pas encore implémenté une méthode, sans modifier la signature de la méthode. Si cela pose un problème, je serais heureux de le savoir, mais cela n'a pas d'importance pour moi : De toute façon, je ne l'utilise qu'en cours de développement, et ses performances ne sont pas si importantes. Néanmoins, je serais heureux d'entendre pourquoi c'est une mauvaise idée, si c'est le cas.

public async Task<object> test()
{
    throw await new AwaitableNotImplementedException<object>();
}

Voici le type que j'ai ajouté pour rendre cela possible.

public class AwaitableNotImplementedException<TResult> : NotImplementedException
{
    public AwaitableNotImplementedException() { }

    public AwaitableNotImplementedException(string message) : base(message) { }

    // This method makes the constructor awaitable.
    public TaskAwaiter<AwaitableNotImplementedException<TResult>> GetAwaiter()
    {
        throw this;
    }
}

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