CA2213: Disposable fields should be disposed

Value
Rule ID CA2213
Category Usage
Fix is breaking or non-breaking Non-breaking

Cause

A type that implements System.IDisposable declares fields that are of types that also implement IDisposable. The Dispose method of the field is not called by the Dispose method of the declaring type.

Rule description

A type is responsible for disposing of all its unmanaged resources. Rule CA2213 checks to see whether a disposable type (that is, one that implements IDisposable) T declares a field F that is an instance of a disposable type FT. For each field F that's assigned a locally created object within the methods or initializers of the containing type T, the rule attempts to locate a call to FT.Dispose. The rule searches the methods called by T.Dispose and one level lower (that is, the methods called by the methods called by T.Dispose).

Note

Other than the special cases, rule CA2213 fires only for fields that are assigned a locally created disposable object within the containing type's methods and initializers. If the object is created or assigned outside of type T, the rule does not fire. This reduces noise for cases where the containing type doesn't own the responsibility for disposing of the object.

Special cases

Rule CA2213 can also fire for fields of the following types even if the object they're assigned isn't created locally:

Passing an object of one of these types to a constructor and then assigning it to a field indicates a dispose ownership transfer to the newly constructed type. That is, the newly constructed type is now responsible for disposing of the object. If the object is not disposed, a violation of CA2213 occurs.

How to fix violations

To fix a violation of this rule, call Dispose on fields that are of types that implement IDisposable.

When to suppress warnings

It's safe to suppress a warning from this rule if:

  • The flagged type is not responsible for releasing the resource held by the field (that is, the type does not have dispose ownership)
  • The call to Dispose occurs at a deeper calling level than the rule checks
  • the dispose ownership of the field(s) is not held by the containing type.

Suppress a warning

If you just want to suppress a single violation, add preprocessor directives to your source file to disable and then re-enable the rule.

#pragma warning disable CA2213
// The code that's violating the rule is on this line.
#pragma warning restore CA2213

To disable the rule for a file, folder, or project, set its severity to none in the configuration file.

[*.{cs,vb}]
dotnet_diagnostic.CA2213.severity = none

To disable this entire category of rules, set the severity for the category to none in the configuration file.

[*.{cs,vb}]
dotnet_analyzer_diagnostic.category-Usage.severity = none

For more information, see How to suppress code analysis warnings.

Example

The following snippet shows a type TypeA that implements IDisposable.

public class TypeA : IDisposable
{
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // Dispose managed resources
        }

        // Free native resources
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Disposable types implement a finalizer.
    ~TypeA()
    {
        Dispose(false);
    }
}

The following snippet shows a type TypeB that violates rule CA2213 by declaring a field aFieldOfADisposableType as a disposable type (TypeA) and not calling Dispose on the field.

public class TypeB : IDisposable
{
    // Assume this type has some unmanaged resources.
    TypeA aFieldOfADisposableType = new TypeA();
    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            // Dispose of resources held by this instance.

            // Violates rule: DisposableFieldsShouldBeDisposed.
            // Should call aFieldOfADisposableType.Dispose();

            disposed = true;
            // Suppress finalization of this disposed instance.
            if (disposing)
            {
                GC.SuppressFinalize(this);
            }
        }
    }

    public void Dispose()
    {
        if (!disposed)
        {
            // Dispose of resources held by this instance.
            Dispose(true);
        }
    }

    // Disposable types implement a finalizer.
    ~TypeB()
    {
        Dispose(false);
    }
}

To fix the violation, call Dispose() on the disposable field:

protected virtual void Dispose(bool disposing)
{
   if (!disposed)
   {
      // Dispose of resources held by this instance.
      aFieldOfADisposableType.Dispose();

      disposed = true;

      // Suppress finalization of this disposed instance.
      if (disposing)
      {
          GC.SuppressFinalize(this);
      }
   }
}

See also