提问人:David L 提问时间:8/3/2022 最后编辑:David L 更新时间:8/9/2022 访问量:144
如果 Fluent 方法链中发生抛出,则带有 Fluent API 的 using 语句将不会调用 Dispose
using statement with fluent api will not call dispose if throw occurs in fluent method chain
问:
将 using 语句与可能引发的 Fluent API 结合使用时,降低的代码将永远不会正确调用 dispose。
如果我有以下公开流畅接口的类:
public class Wrapper : IDisposable
{
private bool _isAdded;
public Wrapper Add()
{
_isAdded = true;
return this;
}
public void Dispose() => Console.WriteLine("dispose called");
public Wrapper ThrowIfAdded() => _isAdded ? throw new Exception() : this;
}
我用以下方式称呼它:
using var willNotDispose = new Wrapper().Add().ThrowIfAdded();
降低的代码将导致在 Fluent 方法链完成后发生 Dispose 调用。
Wrapper willNotDispose = new Wrapper().Add().ThrowIfAdded();
try
{
}
finally
{
if (willNotDispose != null)
{
((IDisposable)willNotDispose).Dispose();
}
}
或者,如果调用是在初始声明之外完成的,.ThrowIfAdded()
using
using var willDispose = new Wrapper().Add();
willDispose.ThrowIfAdded();
降低的代码将按预期生成。
Wrapper willDispose = new Wrapper().Add();
try
{
willDispose.ThrowIfAdded();
}
finally
{
if (willDispose != null)
{
((IDisposable)willDispose).Dispose();
}
}
虽然我理解为什么会发生这种情况,但这是不可取的。有没有办法将前者初始化强制编译为后者?理想情况下,它将是编译器提示的属性或形式,这将导致:
Wrapper willDispose = default;
try
{
willDispose = new Wrapper().Add().ThrowIfAdded();
}
finally
{
if (willDispose != null)
{
((IDisposable)willDispose).Dispose();
}
}
我本来希望原始示例首先编译成。
答:
0赞
David L
8/9/2022
#1
正如注释中指出的,预先存在的指南是,当构造函数中引发异常时,应显式处理该异常并清理资源。
这延伸到以下分析:CA2000
当仅受一个异常处理程序保护的构造函数是 嵌套在 using 语句的 采集部分,在 外部构造函数可以生成由嵌套的 构造函数永远不会关闭。在以下示例中,故障 StreamReader 构造函数可能导致 FileStream 对象永远不会 正在关闭。在这种情况下,CA2000 会标记违反规则的行为。
using (StreamReader sr = new StreamReader(new FileStream("C:/myfile.txt", FileMode.Create)))
{ ... }
虽然抛出异常的 Fluent API 不是显式抛出异常的构造函数或嵌套构造函数,但应将其视为相同,因为对象将在 try/finally 块之外创建和变异。
因此,任何可以引发的方法都必须先调用 dispose,然后才能允许异常传播。
public class Wrapper : IDisposable
{
private bool _isDisposed;
private bool _isAdded;
public Wrapper Add()
{
_isAdded = true;
return this;
}
public void Dispose()
{
if (_isDisposed)
{
return;
}
_isDisposed = true;
Console.WriteLine("dispose called");
}
public Wrapper ThrowIfAdded()
{
if (_isAdded)
{
Dispose();
throw new Exception();
}
return this;
}
}
这正确地确保了在被调用的情况下,将在投掷之前处理掉。.Added()
.ThrowIfAdded()
如果未调用,则实例将按预期在块末尾释放。.Added()
评论
wrapper.Complete();
Add
ThrowIfAdded