CS1998 uyarısını bastır: Bu zaman uyumsuz yöntemde 'await' yok


104

Bazı eşzamansız işlevlere sahip bir arayüzüm var. Arayüzü uygulayan bazı sınıfların bekleyecek hiçbir şeyi yoktur ve bazıları sadece fırlatabilir. Tüm uyarılarla biraz can sıkıcı.

Bir zaman uyumsuz işlevde await kullanılmadığında.

Mesajı bastırmak mümkün mü?

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

uyarı CS1998: Bu zaman uyumsuz yöntem 'bekleme' işleçlerinden yoksundur ve eşzamanlı olarak çalışacaktır. Engellemeyen API çağrılarını beklemek için 'await' operatörünü kullanmayı veya bir arka plan iş parçacığı üzerinde CPU'ya bağlı çalışma yapmak için 'Task.Run (...)' u kullanmayı düşünün.


1
New await anahtar sözcüğünü eşzamansız olarak işaretlenmiş bir işlevde kullanılmadığında.
Simon

Bize sorunu yeniden üreten bir kod örneği göstermeye ne dersiniz?
John Saunders

Yanıtlar:


107

Bazı eşzamansız işlevlere sahip bir arayüzüm var.

Geri dönen yöntemler Taskolduğuna inanıyorum. asyncbir uygulama ayrıntısıdır, bu nedenle arayüz yöntemlerine uygulanamaz.

Arayüzü uygulayan bazı sınıfların bekleyecek hiçbir şeyi yoktur ve bazıları sadece fırlatabilir.

Bu durumlarda, asyncbunun bir uygulama detayı olmasından yararlanabilirsiniz .

Yapacak hiçbir şeyin awaityoksa, geri dönebilirsin Task.FromResult:

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

Atma durumunda NotImplementedException, prosedür biraz daha uzun:

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

Çok fazla yöntem fırlatma yönteminiz varsa NotImplementedException(ki bu, bazı tasarım düzeyinde yeniden düzenlemenin iyi olacağını gösterebilir), o zaman kelimeyi bir yardımcı sınıfa toplayabilirsiniz:

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;
}

Yardımcı sınıf, aynı dönüş türüne sahip her yöntem kendi Taskve NotImplementedExceptionnesnelerini paylaşabileceğinden, aksi takdirde GC'nin toplaması gereken çöpü de azaltır .

AsyncEx kitaplığımda birkaç başka "görev sabiti" türü örneğim var .


1
Anahtar kelimeyi kaybetmeyi düşünmedim. Dediğiniz gibi, eşzamansızın arayüzle hiçbir ilgisi yok. Benim hatam, teşekkür ederim.
Simon

3
Dönüş türünün sadece Görev olduğu bir yaklaşım önerebilir misiniz (sonuçsuz mu?)
Mike

10
Uyarı: Bu yaklaşım sorunlara neden olabilir çünkü hatalar beklediğiniz şekilde yayılmayacaktır. Normalde arayan kişi, yönteminizde bir istisnanın Task. Bunun yerine, yönteminiz bir Task. Gerçekten en iyi modelin operatörü asyncolmayan bir metodu tanımlamak olduğunu düşünüyorum await. Bu, yöntem içindeki kodun tamamının Task.
Bob Meyers

11
CS1998'den kaçınmak await Task.FromResult(0);için yönteminize ekleyebilirsiniz . Bunun önemli bir performans etkisi olmamalıdır (Task.Yield () aksine).
Bob Meyers

3
@AndrewTheken: Bu günlerde sadece yapabilirsiniz return Task.CompletedTask;- en basitini .
Stephen Cleary

65

Diğer bir seçenek, işlevin gövdesini basit tutmak ve onu desteklemek için kod yazmak istemiyorsanız, uyarıyı #pragma ile bastırmaktır:

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

Bu yeterince yaygınsa, devre dışı bırakma ifadesini dosyanın en üstüne koyabilir ve geri yüklemeyi atlayabilirsiniz.

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


43

Eşzamansız anahtar kelimeyi korumanın başka bir yolu da (korumak istemeniz durumunda) kullanmaktır:

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

Yöntemi doldurduktan sonra ifadeyi kaldırabilirsiniz. Bunu özellikle bir yöntem bir şeyi beklediğinde, ancak her uygulama aslında beklemediğinde bunu çok kullanıyorum.


Kabul edilen cevap bu olmalıdır. Bazen arayüz uygulamalarının eşzamansız olması gerekmez, bu her şeyi bir Task.Runçağrıya sarmadan çok daha temizdir .
Andrew Theken

12
Task.CompletedTask bekleyin; // daha iyi bir seçenek olabilir
Frode Nilsen

@FrodeNilsen nedense Task.CompletedTaskartık yok gibi görünüyor.
Sebastián Vansteenkiste

1
@ SebastiánVansteenkiste .Net Framework 4.6->, UWP 1.0->, .Net Core 1.0->
Frode Nilsen

1
@AndrewTheken Bu cevabın ve yorumunuzun özellikle uygulamanın boş olduğu veya sadece bir istisna attığı durum için geçerli olduğu sonucuna varmam biraz zaman aldı (orijinal soruda olduğu gibi). Bir uygulama bir değer döndürürse Task.FromResult, daha iyi cevap gibi görünüyor . Eğer varsa Hatta, olan bir istisna atacak, başka cevap ilişkin devreye giriyor gibi görünüyor Task.FromExceptionbu asla ideal bir çözüm yapma. Katılır mısın
BlueMonkMN

15

Çözümler arasında ve kesinlikle konuşmak gerekirse, arayanın zaman uyumsuz yöntemi nasıl çağıracağını bilmeniz gerekir, ancak yöntem sonucunda ".Wait ()" varsayılan kullanım modeliyle - " Task.CompletedTask'ı döndür " en iyi çözümdür.

    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 |

Not: FromResultdoğrudan karşılaştırılamaz.

Test Kodu:

   [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);
        }
    }

}


1
Bir talihsizlik, #pragmamasrafa girmiş gibi görünüyor. Muhtemelen, geri dönmek yerine CompletedTaskbir AsyncOperation. Derleyiciye, yöntem yine de eşzamanlı olarak çalıştığında bunu atlamanın uygun olduğunu söyleyebilmek güzel olurdu.
binki

Sence Task.CompletedTaskne kadar benzerdir Task.FromResult? Bilmek ilginç olurdu - FromResult'un en çok benzeyen ve bir değer döndürmeniz gerekiyorsa yine de en iyi performans gösteren olmasını bekliyorum.
BlueMonkMN

Ben ekleyeceğim. Ben devlet makine kodu bu durumda daha ayrıntılı olacak düşünmek ve CompletedTask versin bkz kazanacak
Roma Pokrovskij


1
@Tseng. Benchmarkları .NET Core 2.2.0'da çalıştırıyorum. Açıkçası, farklı donanım nedeniyle toplam süre farklı, ancak oran kabaca aynı kalıyor: Yöntem | .NET Core 2.0.3 Ortalama | .NET Core 2.2.0 Ortalama Tamamlandı | % 100 | % 100 TamamlandıBekle | 412.57% | 377,22% FromResult | 520.72% | 590.89% Pragma | 378.37% | 346.64% Verim | 27514.47% | 23602.38%
Fırtına

10

Bunun eski bir iş parçacığı olduğunu biliyorum ve belki de bu, tüm kullanımlar için doğru etkiye sahip olmayacak, ancak aşağıdakiler, henüz bir yöntemi uygulamadığım zaman bir NotImplementedException oluşturabilmem için olabildiğince yakın. yöntem imzasını değiştirmeden. Eğer sorunluysa, bunu bilmek beni mutlu eder, ama benim için çok az önemli: Zaten bunu yalnızca geliştirme aşamasında kullanıyorum, bu yüzden nasıl performans gösterdiği o kadar önemli değil. Yine de, eğer öyleyse, neden kötü bir fikir olduğunu duymaktan mutluluk duyarım.

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

İşte bunu mümkün kılmak için eklediğim tür.

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;
    }
}

10

Stephen Answer'da bir güncelleme TaskConstantsgibi, yeni bir yardımcı yöntem olduğu için artık sınıfı yazmanıza gerek yok :

    public Task ThrowException()
    {
        try
        {
            throw new NotImplementedException();
        }
        catch (Exception e)
        {
            return Task.FromException(e);
        }
    }

3
Bunu yapma. Yığın izleme kodunuzu göstermez. Tamamen başlatılabilmesi için istisnaların atılması gerekir.
Daniel B

1
Daniel B - Evet, kesinlikle haklısınız. İstisnayı doğru şekilde atmak için cevabımı değiştirdim.
Matt

3

Halihazırda Reactive Extension ile bağlantı kurmanız durumunda şunları da yapabilirsiniz:

public async Task<object> NotImplemented()
{
    await Observable.Throw(new NotImplementedException(), null as object).ToTask();
}

public async Task<object> SimpleResult()
{
    await Observable.Return(myvalue).ToTask();
}

Reaktif ve eşzamansız / bekleme, hem kendi başına hem de harikadır, ancak aynı zamanda birlikte iyi oynarlar.

Gerekli olanlar şunlardır:

using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;

3

Aşağıda cs1998 meydana gelebilir.

public async Task<object> Foo()
{
    return object;
}

O zaman aşağıda reform yapabilirsiniz.

public async Task<object> Foo()
{
    var result = await Task.Run(() =>
    {
        return object;
    });
    return result;
}

3

Bunu deneyebilirsin:

public async Task<object> test()
{
await Task.CompletedTask; 
}


1

Bekleyecek bir şeyiniz yoksa Task.FromResult'a geri dönün.

public Task<int> Success() // note: no "async"
{
  ... // Do not have await code
  var result = ...;
  return Task.FromResult(result);
}

1

Yöntem imzanıza bağlı olarak bazı alternatifler aşağıda verilmiştir.

    public async Task Test1()
    {
        await Task.CompletedTask;
    }

    public async Task<object> Test2()
    {
        return await Task.FromResult<object>(null);
    }

    public async Task<object> Test3()
    {
        return await Task.FromException<object>(new NotImplementedException());
    }

-1
// This is to get rid of warning CS1998, please remove when implementing this method.
await new Task(() => { }).ConfigureAwait(false);
throw new NotImplementedException();

-2

Zaman uyumsuz anahtar kelimeyi yöntemden çıkarabilir ve yalnızca Task döndürmesini sağlayabilirsiniz;

    public async Task DoTask()
    {
        State = TaskStates.InProgress;
        await RunTimer();
    }

    public Task RunTimer()
    {
        return new Task(new Action(() =>
        {
            using (var t = new time.Timer(RequiredTime.Milliseconds))
            {
                t.Elapsed += ((x, y) => State = TaskStates.Completed);
                t.Start();
            }
        }));
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.