为什么C#编译器处理空的异步任务方法与返回任务不同?
您好,
编辑:该帖子被编辑为不使用空方法,以更清楚我想知道的内容。
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/uqujuhtask.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/UQuJUhTask.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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在这两个代码中处理异常的方式存在显着差异,因此,不是等效:
如果
console.writeline.writeline
提出了一个例外,则实际上,异常被内部捕获并存储在生成的任务
中,由task.siffed
表示。当您在任务上使用等待
时,该异常实际上是“抛出的”。但是,这种情况是完全同步的。
console.writeline
的潜在异常甚至在返回或处理任何任务之前就会提出。我认为这实际上比第一个选择要少一些,因为此行为可能是出乎意料的。迭代器对屈服中的差异;
和返回array array.empty< object>();
之间的差异表现出相似的行为。现在,您的问题最初询问了实际上是空的方法。好吧,该方法实际上不是空的。它可能在调试模式下包含断点,注入代码或可能对参数的自动生成检查。即使是空的,仍然为一个空物体生成了块:
现在,我可以想象可以将生成
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:
In the case that
Console.WriteLine
raised an exception, the exception is actually internally caught and stored inside the resultingTask
, indicated byTask.IsFaulted
. The exception is only actually "thrown" when you useawait
on the task.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 betweenyield break;
andreturn 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: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 emptyasync
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 theasync
/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.)