Share via


CA2000:超出范围前释放对象

类型名

DisposeObjectsBeforeLosingScope

CheckId

CA2000

类别

Microsoft.Reliability

是否重大更改

非重大更改

原因

创建了 IDisposable 类型的局部对象,但在对该对象的所有引用超出范围之前没有释放该对象。

规则说明

如果在对可释放对象的所有引用超出范围之前没有显式释放该对象,当垃圾回收器运行该对象的终结器时,将在某个不确定的时间释放该对象。 由于可能发生异常事件,导致对象的终结器无法运行,因此,应选择显式释放对象。

如何解决冲突

要修复与该规则的冲突,请在对对象的所有引用超出范围之前对该对象调用 Dispose

请注意,您可以使用 using 语句(在 Visual Basic 中为 Using)来包装实现 IDisposable 的对象。 通过此方式包装的对象将在关闭 using 块时自动释放。

在下列情况下,using 语句不足以保护 IDisposable 对象,并且可能会导致引发 CA2000。

  • 返回可释放对象要求在 using 块以外的 try/finally 块中构造该对象。

  • 不应在一个使用语句的构造函数中完成对可释放对象的成员的初始化。

  • 只能由一个异常处理程序嵌套受保护的构造函数。 例如,

    using (StreamReader sr = new StreamReader(new FileStream("C:\myfile.txt", FileMode.Create)))
    { ... }
    

    导致引发 CA2000,这是因为 StreamReader 对象结构中的故障可能会导致 FileStream 对象永远不被关闭。

  • 动态对象应使用阴影对象来实现 IDisposable 对象的 Dispose 模式。

何时禁止显示警告

不要禁止显示此规则发出的警告,除非您对您的对象调用了一个方法,而该方法调用 Dispose,如 Close.

相关规则

CA2213:应释放可释放的字段

CA2202:不要多次释放对象

示例

如果正在实施一个返回可释放对象的方法,使用不包含 catch 块的 try/finally 块以确保对象已释放。 通过使用 try/finally 块,允许在 fault 点引发异常,并确保释放该对象。

在 OpenPort 方法中,打开 ISerializable 对象 SerialPort 的调用或 SomeMethod 的调用可能会失败。 在此实现上引发 CA2000 警告。

在 OpenPort2 方法中,两个 SerialPort 对象被声明并设置为 null:

  • tempPort 用于测试该方法操作是否成功。

  • port,它用于方法的返回值。

tempPort 是在 try 块中构造并打开的,并且任何其他所需工作也是在相同的 try 块中执行的。 在 try 块的末尾,开放端口分配给将返回的 port 对象,并且 tempPort 对象设置为 null。

finally 块将检查 tempPort 的值。 如果它不为 null,则方法中的操作失败,并且 tempPort 关闭以确保任何资源都被释放。 如果该方法的操作成功,则返回的端口对象将包含打开的 SerialPort 对象,或者,如果操作失败,则将为空。

Public Function OpenPort1(ByVal PortName As String) As SerialPort

   Dim port As New SerialPort(PortName)
   port.Open()    'CA2000 fires because this might throw
   SomeMethod()   'Other method operations can fail
   Return port

End Function


Public Function OpenPort2(ByVal PortName As String) As SerialPort

   Dim tempPort As SerialPort = Nothing
   Dim port As SerialPort = Nothing

   Try
      tempPort = New SerialPort(PortName)
      tempPort.Open()
      SomeMethod()
      'Add any other methods above this line
      port = tempPort
      tempPort = Nothing

   Finally
      If Not tempPort Is Nothing Then
         tempPort.Close()
      End If


   End Try

   Return port

End Function

Public Function CreateReader1(ByVal x As Integer) As StreamReader
   Dim local As New StreamReader("C:\Temp.txt")
   x += 1
   Return local
End Function


Public Function CreateReader2(ByVal x As Integer) As StreamReader
   Dim local As StreamReader = Nothing
   Dim localTemp As StreamReader = Nothing
   Try
      localTemp = New StreamReader("C:\Temp.txt")
      x += 1
      local = localTemp
      localTemp = Nothing
      Finally
         If (Not (localTemp Is Nothing)) Then
            localTemp.Dispose()
         End If
   End Try
   Return local
End Function

public SerialPort OpenPort1(string portName)
{
   SerialPort port = new SerialPort(portName);
   port.Open();  //CA2000 fires because this might throw
   SomeMethod(); //Other method operations can fail
   return port;
}

public SerialPort OpenPort2(string portName)
{
   SerialPort tempPort = null;
   SerialPort port = null;
   try
   {
      tempPort = new SerialPort(portName);
      tempPort.Open();
      SomeMethod();
      //Add any other methods above this line
      port = tempPort;
      tempPort = null;

   }
   finally
   {
      if (tempPort != null)
      {
         tempPort.Close();
      }
   }
   return port;
}

默认情况下,Visual Basic 编译器检查所有算术运算符是否发生溢出。 因此,任何 Visual Basic 算术运算都可能引发一个 OverflowException。 这可能导致规则(如 CA2000)上的意外冲突。 例如,下面的 CreateReader1 函数会产生 CA2000 冲突,因为 Visual Basic 编译器针对可能引发导致 StreamReader 不被释放的异常的添加发出溢出检查指令。

若要解决此问题,您可以在您的项目中禁用通过 Visual Basic 编译器发出溢出检查,或者您可以将您的代码修改为与以下 CreateReader2 函数一样。

若要禁用发出溢出检查,请在“解决方案资源管理器”中右击项目名称,再单击**“属性”。 依次单击“编译”“高级编译选项”“不做整数溢出检查”**。

请参见

参考

IDisposable

实现 Finalize 和 Dispose 以清理非托管资源