When writing managed code, there is no need to worry about memory
allocation or deallocation as it is taken care of by the garbage
collector. However, certain objects, such as Bitmap
, utilize unmanaged memory for specific purposes like pointer arithmetic. These objects may have substantial
unmanaged memory footprints while having minimal managed footprints. Unfortunately, the garbage collector only recognizes the small managed footprint
and does not promptly reclaim the corresponding unmanaged memory (by invoking the finalizer method of Bitmap
) for efficiency reasons.
In addition, it’s essential to manage other system resources besides memory. The operating system has limits on the number of file descriptors (e.g., FileStream
) or sockets (e.g., WebClient
) that can remain open simultaneously. Therefore, it’s
crucial to Dispose
of these resources promptly when they are no longer required, instead of relying on the garbage collector to invoke
the finalizers of these objects at an unpredictable time in the future.
This rule keeps track of private
fields and local variables of specific types that implement IDisposable
or
IAsyncDisposable
. It identifies instances of these types that are not properly disposed, closed, aliased, returned, or passed to other
methods. This applies to instances that are either directly created using the new
operator or instantiated through a predefined list of
factory methods.
Here is the list of predefined factory methods tracked by this rule:
-
System.IO.File.Create()
-
System.IO.File.Open()
-
System.Drawing.Image.FromFile()
-
System.Drawing.Image.FromStream()
Exceptions
IDisposable
/ IAsyncDisposable
variables returned from a method or passed to other methods are ignored, as are local
IDisposable
/ IAsyncDisposable
objects that are initialized with other IDisposable
/
IAsyncDisposable
objects.
public Stream WriteToFile(string path, string text)
{
var fs = new FileStream(path, FileMode.Open); // Compliant: it is returned
var bytes = Encoding.UTF8.GetBytes(text);
fs.Write(bytes, 0, bytes.Length);
return fs;
}
public void ReadFromStream(Stream s)
{
var sr = new StreamReader(s); // Compliant: it would close the underlying stream.
// ...
}