配置Await(false)和IAsyncDisposable的结构体实现
我已经使用 ActionOnAsyncDispose 结构实现了 IAsyncDisposable,如下所示。我的理解是,当它处于 async using 语句中时,编译器不会将其装箱:
ActionOnDisposeAsync x = ...;
await using (x) {
...
}
正确吗?到目前为止,一切都很好。我的问题是,当我像这样配置等待时:
ActionOnDisposeAsync x = ...;
await using (x.ConfigureAwait()) {
...
}
x 会被装箱吗?如果我将ConfigureAwait 放入方法Caf() 中会怎么样:
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static public ConfiguredAsyncDisposable Caf(this ActionOnDisposeAsync disposable)
=> disposable.ConfigureAwait(false);
ActionOnDisposeAsync x = ...;
await using (x.Caf()) {
...
}
在这种情况下我可以避免装箱吗?我无法找到关于我的 using 变量到底需要实现什么才能实现ConfigureAwait 效果的文档。似乎也没有任何构建ConfiguredAsyncDisposable 的公共方法。
这是 ActionOnDisposeAsync:
public readonly struct ActionOnDisposeAsync : IAsyncDisposable, IEquatable<ActionOnDisposeAsync>
{
public ActionOnDisposeAsync(Func<Task> actionAsync)
{
this.ActionAsync = actionAsync;
}
public ActionOnDisposeAsync( Action actionSync)
{
this.ActionAsync = () => { actionSync(); return Task.CompletedTask; };
}
private Func<Task> ActionAsync { get; }
public async ValueTask DisposeAsync()
{
if (this.ActionAsync != null) {
await this.ActionAsync();
}
}
...
}
I have implemented IAsyncDisposable with an ActionOnAsyncDispose struct as shown below. My understanding is that the compiler will not box it when it is in an async using statement:
ActionOnDisposeAsync x = ...;
await using (x) {
...
}
Correct? So far so good. My question is this, when I configure await on it like so:
ActionOnDisposeAsync x = ...;
await using (x.ConfigureAwait()) {
...
}
will x be boxed? What about if I put the ConfigureAwait in a method, Caf():
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static public ConfiguredAsyncDisposable Caf(this ActionOnDisposeAsync disposable)
=> disposable.ConfigureAwait(false);
ActionOnDisposeAsync x = ...;
await using (x.Caf()) {
...
}
Can I avoid boxing in that case? I was not able to find documentation on what exactly my using variable needs to implement in order to have the effect of ConfigureAwait. There doesn't seem to be any public way of constructing a ConfiguredAsyncDisposable either.
Here is ActionOnDisposeAsync:
public readonly struct ActionOnDisposeAsync : IAsyncDisposable, IEquatable<ActionOnDisposeAsync>
{
public ActionOnDisposeAsync(Func<Task> actionAsync)
{
this.ActionAsync = actionAsync;
}
public ActionOnDisposeAsync( Action actionSync)
{
this.ActionAsync = () => { actionSync(); return Task.CompletedTask; };
}
private Func<Task> ActionAsync { get; }
public async ValueTask DisposeAsync()
{
if (this.ActionAsync != null) {
await this.ActionAsync();
}
}
...
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
是的,
ConfigureAwait
在struct
上一次性使用会导致装箱。以下是此行为的实验演示:...其中
MyDisposableStruct
是这个简单的结构:输出:
现场演示。
为了防止装箱发生,您必须创建一个自定义
ConfiguredAsyncDisposable
类结构,专为您的结构定制。实现方法如下:现在运行与之前相同的实验,无需对代码进行任何更改,也不会导致分配。输出为:
现场演示。
Yes, the
ConfigureAwait
onstruct
disposables causes boxing. Here is an experimental demonstration of this behavior:...where
MyDisposableStruct
is this simple struct:Output:
Live demo.
To prevent the boxing from happening you will have to create a custom
ConfiguredAsyncDisposable
-like struct, that is tailored specifically for your struct. Here is how it can be done:Now running the same experiment as before, without making any change in the code whatsoever, does not cause allocations. The output is:
Live demo.
如果编译器能够检测实际类型(您的结构),则不需要拳击。如果仅通过界面工作,则在处置时会进行。我的检查您的编译代码是否使用ILSPY之类的内容,您将查看是否在类(也是接口的情况)或值类型(/struct)上完成的Distose语句。
我不确定使用结构是否会在处置异步时会获得很多收益,并且是否值得付出努力,但是在决定之前,您应该衡量它。
If the compiler is able to detect the actual type (your struct) it does not require boxing. If it only works via the interface, it will when disposing. My checking your compiled code with something like ILSpy you will see if the dispose statement is done on a class (also the case for interfaces), or on a value type (/struct).
I'm not sure if using a struct will gain you much when disposing async, and if it is worth the effort, but you should measure that before deciding.