为什么C#编译器处理空的异步任务方法与返回任务不同?

发布于 2025-02-12 04:49:12 字数 968 浏览 2 评论 0原文

您好,

编辑:该帖子被编辑为不使用空方法,以更清楚我想知道的内容。

C#编译不使用方法不使用等待返回 task> task.pletedTask 的情况下,C#编译没有编译async任务方法的原因吗?

有时,有必要实现一种返回任务的方法,但它不使用等待

我想知道这两种方法之间生成的IL代码是否如此不同的原因是否有一个原因:

public async Task DoNothing()
{
    Console.WriteLine("nothing");
}
public Task DoNothing()
{
    Console.WriteLine("nothing");
    return Task.CompletedTask;
}

我创建了两个可以查看生成的IL代码的提琴。
async任务 httpps://dotnetfiddle.net/uqujuh


task.completedTask https://dotnetfiddle.net/err4i1

为什么编译器不优化代码以相同处理两种情况的原因? 谢谢。

Hello,

Edit: the post was edited to not use empty methods to be more clear about what I want to know.

Is there a reason why the C# compile does not compile a async Task method without using await to return Task.CompletedTask?

Sometimes it is necessary to implement a method that returns a Task but it is not using await.

I am wondering if there is a reason why the generated IL code is so different between that two methods:

public async Task DoNothing()
{
    Console.WriteLine("nothing");
}
public Task DoNothing()
{
    Console.WriteLine("nothing");
    return Task.CompletedTask;
}

I created two Fiddles where the generated IL code can be viewed.
async Task: https://dotnetfiddle.net/UQuJUh
Task.CompletedTask: https://dotnetfiddle.net/eRr4i1

So is there a reason why the compiler does not optimize the code to handle both cases the same way?
Thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

拒绝两难 2025-02-19 04:49:12

在这两个代码中处理异常的方式存在显着差异,因此,不是等效

public async Task DoNothing()
{
    Console.WriteLine("nothing");
}

如果console.writeline.writeline提出了一个例外,则实际上,异常被内部捕获并存储在生成的任务中,由task.siffed表示。当您在任务上使用等待时,该异常实际上是“抛出的”。

public Task DoNothing()
{
    Console.WriteLine("nothing");
    return Task.CompletedTask;
}

但是,这种情况是完全同步的。 console.writeline的潜在异常甚至在返回或处理任何任务之前就会提出。我认为这实际上比第一个选择要少一些,因为此行为可能是出乎意料的。迭代器对屈服中的差异;返回array array.empty< object>();之间的差异表现出相似的行为。

现在,您的问题最初询问了实际上是空的方法。好吧,该方法实际上不是空的。它可能在调试模式下包含断点,注入代码或可能对参数的自动生成检查。即使是空的,仍然为一个空物体生成了块:

  .try
  {
    IL_0000:  leave.s    IL_0019
  }  // end .try
  catch [mscorlib]System.Exception 
  {

现在,我可以想象可以将生成async方法 的例外对于真正空的方法的情况,编译器,但这只会使代码复杂化,而没有(可以说)真实的收益。可能有一个空的async方法(例如,给其他可能想在未来提示中实现它的程序员),但是您不需要关心性能很大程度上需要这种优化(无论如何您都有一个微不足道的解决方案)。

(有趣的旁注:C#11可能会引入参数nullexception从带有> !!的参数。 > async/迭代方法的代码立即在调用后,因此可以说,方法主体或它变成了什么,从技术上讲,它仍然是空的。)

There is a significant difference in the way exceptions are handled in those two pieces of code, which are therefore not equivalent:

public async Task DoNothing()
{
    Console.WriteLine("nothing");
}

In the case that Console.WriteLine raised an exception, the exception is actually internally caught and stored inside the resulting Task, indicated by Task.IsFaulted. The exception is only actually "thrown" when you use await on the task.

public Task DoNothing()
{
    Console.WriteLine("nothing");
    return Task.CompletedTask;
}

This case however is fully synchronous; the potential exception from Console.WriteLine would be raised even before any task could be returned or processed. I'd argue this is actually a bit less safe than the first option, since this behaviour might be unexpected. Iterators show similar behaviour for the difference between yield break; and return Array.Empty<object>();.

Now your question originally asked about an actually empty method. Well, it could be the case that the method is not actually empty; it may contain breakpoints in debug mode, injected code or perhaps auto-generated checks for the arguments, in the future. Even the empty try block is still generated for an empty method body:

  .try
  {
    IL_0000:  leave.s    IL_0019
  }  // end .try
  catch [mscorlib]System.Exception 
  {

Now I can imagine an exception in generating async method could be added to the compiler for the case of a truly empty method, but it would only complicate the code, for no (arguably) real gain. There might be a point of having an empty async method (such as to give other programmers who might want to implement it in a future a hint), but then you don't need to care about performance that much to warrant such an optimization (and you have a trivial solution anyway).

(Interesting side note: C# 11 might introduce automatic raising of ArgumentNullException from parameters marked with !!. At this time, the exception is actually raised outside of the async/iterator method's code, immediately after the call, so it could be argued that the method body, or what becomes of it, is still, technically, empty.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文