CA1063:正确实现 IDisposable
属性 | 值 |
---|---|
规则 ID | CA1063 |
标题 | 正确实现 IDisposable |
类别 | 设计 |
修复是中断修复还是非中断修复 | 非中断 |
在 .NET 8 中默认启用 | 否 |
原因
System.IDisposable 接口无法正确实现。 可能的原因包括:
- 在类中重新实现 IDisposable。
- 再次重写
Finalize
。 - 重写
Dispose()
。 Dispose()
方法是非公用、已密封或命名为“Dispose”。Dispose(bool)
未受保护、虚拟或未密封。- 在未密封类型中,
Dispose()
必须调用Dispose(true)
。 - 对于未密封的类型,
Finalize
实现不调用或不同时调用Dispose(bool)
或基类终结器。
违反其中任何一个模式都会触发警告 CA1063。
声明和实现 IDisposable 接口的每个未密封类型都必须提供自己的 protected virtual void Dispose(bool)
方法。 Dispose()
应该调用 Dispose(true)
,而终结器应该调用 Dispose(false)
。 如果创建声明和实现 IDisposable 接口的未密封类型,则必须对 Dispose(bool)
进行定义和调用。 有关详细信息,请参阅清理非托管资源(.NET 指南)和实现 Dispose 模式。
默认情况下,此规则仅查看外部可见的类型,但这是可配置的。
规则说明
所有 IDisposable 类型都应正确实现 Dispose 模式。
如何解决冲突
检查代码,并确定以下哪种解决方法能解决此冲突:
从类型实现的接口列表中移除 IDisposable,并重写 Dispose 基类实现。
从类型中移除终结器,重写 Dispose(bool disposing),并在“disposing”为 false 的代码路径中加入终结逻辑。
重写 Dispose(bool disposing),并在“disposing”为 true 的代码路径中加入释放逻辑。
确保将 Dispose() 声明为公用且已密封。
将 dispose 方法重命名为“Dispose”,并确保将其声明为公用且已密封。
确保 Dispose(bool) 声明为受保护、虚拟和未密封。
修改 Dispose(),使其调用 Dispose(true),并在当前对象实例(在 Visual Basic 中为
this
或Me
)上调用 SuppressFinalize,然后返回。修改终结器,使其调用 Dispose(false),然后返回。
如果创建声明和实现 IDisposable 接口的未密封类型,请确保 IDisposable 的实现遵循本节前面所介绍的模式。
何时禁止显示警告
不禁止显示此规则发出的警告。
注意
如果满足以下所有条件,你可能会看到来自此规则的误报警告:
- 你将 Visual Studio 2022 版本 17.5 或更高版本与旧版 .NET SDK(即 .NET 6 或更低版本)配合使用。
- 你使用的是 .NET 6 SDK 中的分析器或更低版本的分析器包,例如 Microsoft.CodeAnalysis.FxCopAnalyzers。
- 你在
IDispose
实现上有属性。
在这种情况下,禁止显示误报警告没有风险。 误报是由于 C# 编译器中的中断性变更造成的。 请考虑使用更新的包含误报警告修补程序的分析器。 请升级到 Microsoft.CodeAnalysis.NetAnalyzers 版本 7.0.0-preview1.22464.1 或更高版本,或者使用 .NET 7 SDK 中的分析器。
配置代码以进行分析
使用下面的选项来配置代码库的哪些部分要运行此规则。
可以仅为此规则、为适用的所有规则或为适用的此类别(设计)中的所有规则配置此选项。 有关详细信息,请参阅代码质量规则配置选项。
包含特定的 API 图面
你可以根据代码库的可访问性,配置要针对其运行此规则的部分。 例如,若要指定规则应仅针对非公共 API 图面运行,请将以下键值对添加到项目中的 .editorconfig 文件:
dotnet_code_quality.CAXXXX.api_surface = private, internal
伪代码示例
以下伪代码提供了有关如何在使用托管资源和本机资源的类中实现 Dispose(bool)
的常规示例。
public class Resource : IDisposable
{
private bool isDisposed;
private IntPtr nativeResource = Marshal.AllocHGlobal(100);
private AnotherResource managedResource = new AnotherResource();
// Dispose() calls Dispose(true)
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// The bulk of the clean-up code is implemented in Dispose(bool)
protected virtual void Dispose(bool disposing)
{
if (isDisposed) return;
if (disposing)
{
// free managed resources
managedResource.Dispose();
}
// free native resources if there are any.
if (nativeResource != IntPtr.Zero)
{
Marshal.FreeHGlobal(nativeResource);
nativeResource = IntPtr.Zero;
}
isDisposed = true;
}
// NOTE: Leave out the finalizer altogether if this class doesn't
// own unmanaged resources, but leave the other methods
// exactly as they are.
~Resource()
{
// Finalizer calls Dispose(false)
Dispose(false);
}
}