Skip to content

Commit ec1c4f8

Browse files
* Fixes dotnet#35935 * More fixes * Apply suggestions from code review Co-authored-by: Bill Wagner <wiwagn@microsoft.com> --------- Co-authored-by: Bill Wagner <wiwagn@microsoft.com>
1 parent 29260f3 commit ec1c4f8

File tree

10 files changed

+95
-81
lines changed

10 files changed

+95
-81
lines changed

docs/standard/garbage-collection/implementing-dispose.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "Implement a Dispose method"
33
description: In this article, learn to implement the Dispose method, which releases unmanaged resources used by your code in .NET.
4-
ms.date: 02/16/2023
4+
ms.date: 07/20/2023
55
dev_langs:
66
- "csharp"
77
- "vb"
@@ -13,11 +13,11 @@ ms.topic: how-to
1313

1414
# Implement a Dispose method
1515

16-
The <xref:System.IDisposable.Dispose%2A> method is primarily implemented to release unmanaged resources. When working with instance members that are <xref:System.IDisposable> implementations, it's common to cascade <xref:System.IDisposable.Dispose%2A> calls. There are additional reasons for implementing <xref:System.IDisposable.Dispose%2A>, for example, to free memory that was allocated, remove an item that was added to a collection, or signal the release of a lock that was acquired.
16+
The <xref:System.IDisposable.Dispose%2A> method is primarily implemented to release unmanaged resources. When working with instance members that are <xref:System.IDisposable> implementations, it's common to cascade <xref:System.IDisposable.Dispose%2A> calls. There are other reasons for implementing <xref:System.IDisposable.Dispose%2A>, for example, to free memory that was allocated, remove an item that was added to a collection, or signal the release of a lock that was acquired.
1717

18-
The [.NET garbage collector](index.md) does not allocate or release unmanaged memory. The pattern for disposing an object, referred to as the dispose pattern, imposes order on the lifetime of an object. The dispose pattern is used for objects that implement the <xref:System.IDisposable> interface. This pattern is common when interacting with file and pipe handles, registry handles, wait handles, or pointers to blocks of unmanaged memory, because the garbage collector is unable to reclaim unmanaged objects.
18+
The [.NET garbage collector](index.md) doesn't allocate or release unmanaged memory. The pattern for disposing an object, referred to as the dispose pattern, imposes order on the lifetime of an object. The dispose pattern is used for objects that implement the <xref:System.IDisposable> interface. This pattern is common when interacting with file and pipe handles, registry handles, wait handles, or pointers to blocks of unmanaged memory, because the garbage collector is unable to reclaim unmanaged objects.
1919

20-
To help ensure that resources are always cleaned up appropriately, a <xref:System.IDisposable.Dispose%2A> method should be idempotent, such that it is callable multiple times without throwing an exception. Furthermore, subsequent invocations of <xref:System.IDisposable.Dispose%2A> should do nothing.
20+
To help ensure that resources are always cleaned up appropriately, a <xref:System.IDisposable.Dispose%2A> method should be idempotent, such that it's callable multiple times without throwing an exception. Furthermore, subsequent invocations of <xref:System.IDisposable.Dispose%2A> should do nothing.
2121

2222
The code example provided for the <xref:System.GC.KeepAlive%2A?displayProperty=nameWithType> method shows how garbage collection can cause a finalizer to run while an unmanaged reference to the object or its members is still in use. It may make sense to utilize <xref:System.GC.KeepAlive%2A?displayProperty=nameWithType> to make the object ineligible for garbage collection from the start of the current routine to the point where this method is called.
2323

@@ -29,7 +29,7 @@ Writing code for an object's finalizer is a complex task that can cause problems
2929

3030
A <xref:System.Runtime.InteropServices.SafeHandle?displayProperty=nameWithType> is an abstract managed type that wraps an <xref:System.IntPtr?displayProperty=nameWithType> that identifies an unmanaged resource. On Windows it might identify a handle, and on Unix, a file descriptor. The `SafeHandle` provides all of the logic necessary to ensure that this resource is released once and only once, either when the `SafeHandle` is disposed of or when all references to the `SafeHandle` have been dropped and the `SafeHandle` instance is finalized.
3131

32-
The <xref:System.Runtime.InteropServices.SafeHandle?displayProperty=nameWithType> is an abstract base class. Derived classes provide specific instances for different kinds of handle. These derived classes validate what values for the <xref:System.IntPtr?displayProperty=nameWithType> are considered invalid and how to actually free the handle. For example, <xref:Microsoft.Win32.SafeHandles.SafeFileHandle> derives from `SafeHandle` to wrap `IntPtrs` that identify open file handles/descriptors, and overrides its <xref:System.Runtime.InteropServices.SafeHandle.ReleaseHandle?displayProperty=nameWithType> method to close it (via the `close` function on Unix or `CloseHandle` function on Windows). Most APIs in .NET libraries that create an unmanaged resource will wrap it in a `SafeHandle` and return that `SafeHandle` to you as needed, rather than handing back the raw pointer. In situations where you interact with an unmanaged component and get an `IntPtr` for an unmanaged resource, you can create your own `SafeHandle` type to wrap it. As a result, few non-`SafeHandle` types need to implement finalizers. Most disposable pattern implementations only end up wrapping other managed resources, some of which may be `SafeHandle` objects.
32+
The <xref:System.Runtime.InteropServices.SafeHandle?displayProperty=nameWithType> is an abstract base class. Derived classes provide specific instances for different kinds of handle. These derived classes validate what values for the <xref:System.IntPtr?displayProperty=nameWithType> are considered invalid and how to actually free the handle. For example, <xref:Microsoft.Win32.SafeHandles.SafeFileHandle> derives from `SafeHandle` to wrap `IntPtrs` that identify open file handles/descriptors, and overrides its <xref:System.Runtime.InteropServices.SafeHandle.ReleaseHandle?displayProperty=nameWithType> method to close it (via the `close` function on Unix or `CloseHandle` function on Windows). Most APIs in .NET libraries that create an unmanaged resource wraps it in a `SafeHandle` and return that `SafeHandle` to you as needed, rather than handing back the raw pointer. In situations where you interact with an unmanaged component and get an `IntPtr` for an unmanaged resource, you can create your own `SafeHandle` type to wrap it. As a result, few non-`SafeHandle` types need to implement finalizers. Most disposable pattern implementations only end up wrapping other managed resources, some of which may be `SafeHandle` objects.
3333

3434
The following derived classes in the <xref:Microsoft.Win32.SafeHandles> namespace provide safe handles.
3535

@@ -43,7 +43,7 @@ The following derived classes in the <xref:Microsoft.Win32.SafeHandles> namespac
4343

4444
## Dispose() and Dispose(bool)
4545

46-
The <xref:System.IDisposable> interface requires the implementation of a single parameterless method, <xref:System.IDisposable.Dispose%2A>. Also, any non-sealed class should have an additional `Dispose(bool)` overload method.
46+
The <xref:System.IDisposable> interface requires the implementation of a single parameterless method, <xref:System.IDisposable.Dispose%2A>. Also, any non-sealed class should have an `Dispose(bool)` overload method.
4747

4848
Method signatures are:
4949

@@ -52,12 +52,12 @@ Method signatures are:
5252

5353
### The Dispose() method
5454

55-
Because the `public`, non-virtual (`NotOverridable` in Visual Basic), parameterless `Dispose` method is called when it is no longer needed (by a consumer of the type), its purpose is to free unmanaged resources, perform general cleanup, and to indicate that the finalizer, if one is present, doesn't have to run. Freeing the actual memory associated with a managed object is always the domain of the [garbage collector](index.md). Because of this, it has a standard implementation:
55+
Because the `public`, non-virtual (`NotOverridable` in Visual Basic), parameterless `Dispose` method is called when it's no longer needed (by a consumer of the type), its purpose is to free unmanaged resources, perform general cleanup, and to indicate that the finalizer, if one is present, doesn't have to run. Freeing the actual memory associated with a managed object is always the domain of the [garbage collector](index.md). Because of this, it has a standard implementation:
5656

5757
:::code language="csharp" source="../../../samples/snippets/csharp/VS_Snippets_CLR/conceptual.disposable/cs/Disposable.cs" id="Dispose":::
5858
:::code language="vb" source="../../../samples/snippets/visualbasic/VS_Snippets_CLR/conceptual.disposable/vb/Disposable.vb" id="Dispose":::
5959

60-
The `Dispose` method performs all object cleanup, so the garbage collector no longer needs to call the objects' <xref:System.Object.Finalize%2A?displayProperty=nameWithType> override. Therefore, the call to the <xref:System.GC.SuppressFinalize%2A> method prevents the garbage collector from running the finalizer. If the type has no finalizer, the call to <xref:System.GC.SuppressFinalize%2A?displayProperty=nameWithType> has no effect. Note that the actual cleanup is performed by the `Dispose(bool)` method overload.
60+
The `Dispose` method performs all object cleanup, so the garbage collector no longer needs to call the objects' <xref:System.Object.Finalize%2A?displayProperty=nameWithType> override. Therefore, the call to the <xref:System.GC.SuppressFinalize%2A> method prevents the garbage collector from running the finalizer. If the type has no finalizer, the call to <xref:System.GC.SuppressFinalize%2A?displayProperty=nameWithType> has no effect. The actual cleanup is performed by the `Dispose(bool)` method overload.
6161

6262
### The Dispose(bool) method overload
6363

@@ -77,9 +77,9 @@ The body of the method consists of three blocks of code:
7777

7878
- **Managed objects that implement <xref:System.IDisposable>.** The conditional block can be used to call their <xref:System.IDisposable.Dispose%2A> implementation (cascade dispose). If you have used a derived class of <xref:System.Runtime.InteropServices.SafeHandle?displayProperty=nameWithType> to wrap your unmanaged resource, you should call the <xref:System.Runtime.InteropServices.SafeHandle.Dispose?displayProperty=nameWithType> implementation here.
7979

80-
- **Managed objects that consume large amounts of memory or consume scarce resources.** Assign large managed object references to `null` to make them more likely to be unreachable. This releases them faster than if they were reclaimed non-deterministically.
80+
- **Managed objects that consume large amounts of memory or consume scarce resources.** Assign large managed object references to `null` to make them more likely to be unreachable. This releases them faster than if they were reclaimed nondeterministically.
8181

82-
If the method call comes from a finalizer, only the code that frees unmanaged resources should execute. The implementer is responsible for ensuring that the false path doesn't interact with managed objects that may have been disposed. This is important because the order in which the garbage collector disposes managed objects during finalization is non-deterministic.
82+
If the method call comes from a finalizer, only the code that frees unmanaged resources should execute. The implementer is responsible for ensuring that the false path doesn't interact with managed objects that may have been disposed. This is important because the order in which the garbage collector disposes managed objects during finalization is nondeterministic.
8383

8484
## Cascade dispose calls
8585

@@ -104,7 +104,7 @@ All non-sealed classes (or Visual Basic classes not modified as `NotInheritable`
104104
> [!IMPORTANT]
105105
> It's possible for a base class to only reference managed objects and implement the dispose pattern. In these cases, a finalizer is unnecessary. A finalizer is only required if you directly reference unmanaged resources.
106106
107-
Here's an example of the general pattern for implementing the dispose pattern for a base class that uses a safe handle.
107+
Here's a general example of implementing the dispose pattern for a base class that uses a safe handle.
108108

109109
:::code language="csharp" source="../../../samples/snippets/csharp/VS_Snippets_CLR_System/system.idisposable/cs/base1.cs":::
110110
:::code language="vb" source="../../../samples/snippets/visualbasic/VS_Snippets_CLR_System/system.idisposable/vb/base1.vb":::

samples/snippets/csharp/VS_Snippets_CLR_System/system.idisposable/cs/base1.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22
using System;
33
using System.Runtime.InteropServices;
44

5-
class BaseClassWithSafeHandle : IDisposable
5+
public class BaseClassWithSafeHandle : IDisposable
66
{
77
// To detect redundant calls
88
private bool _disposedValue;
99

1010
// Instantiate a SafeHandle instance.
11-
private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);
11+
private SafeHandle? _safeHandle = new SafeFileHandle(IntPtr.Zero, true);
1212

1313
// Public implementation of Dispose pattern callable by consumers.
14-
public void Dispose() => Dispose(true);
14+
public void Dispose()
15+
{
16+
Dispose(true);
17+
GC.SuppressFinalize(this);
18+
}
1519

1620
// Protected implementation of Dispose pattern.
1721
protected virtual void Dispose(bool disposing)
@@ -20,7 +24,8 @@ protected virtual void Dispose(bool disposing)
2024
{
2125
if (disposing)
2226
{
23-
_safeHandle.Dispose();
27+
_safeHandle?.Dispose();
28+
_safeHandle = null;
2429
}
2530

2631
_disposedValue = true;

samples/snippets/csharp/VS_Snippets_CLR_System/system.idisposable/cs/base2.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System;
22

3-
class BaseClassWithFinalizer : IDisposable
3+
public class BaseClassWithFinalizer : IDisposable
44
{
55
// To detect redundant calls
66
private bool _disposedValue;

samples/snippets/csharp/VS_Snippets_CLR_System/system.idisposable/cs/calling2.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ public WordCountCleanup(string filename)
1414

1515
FullName = filename;
1616

17-
var txt = string.Empty;
18-
StreamReader streamReader = null;
17+
string txt = string.Empty;
18+
StreamReader? streamReader = null;
1919
try
2020
{
2121
streamReader = new StreamReader(filename);

samples/snippets/csharp/VS_Snippets_CLR_System/system.idisposable/cs/derived1.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
using System;
33
using System.Runtime.InteropServices;
44

5-
class DerivedClassWithSafeHandle : BaseClassWithSafeHandle
5+
public class DerivedClassWithSafeHandle : BaseClassWithSafeHandle
66
{
77
// To detect redundant calls
88
private bool _disposedValue;
99

1010
// Instantiate a SafeHandle instance.
11-
private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);
11+
private SafeHandle? _safeHandle = new SafeFileHandle(IntPtr.Zero, true);
1212

1313
// Protected implementation of Dispose pattern.
1414
protected override void Dispose(bool disposing)
@@ -17,7 +17,8 @@ protected override void Dispose(bool disposing)
1717
{
1818
if (disposing)
1919
{
20-
_safeHandle.Dispose();
20+
_safeHandle?.Dispose();
21+
_safeHandle = null;
2122
}
2223

2324
_disposedValue = true;

samples/snippets/csharp/VS_Snippets_CLR_System/system.idisposable/cs/derived2.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
class DerivedClassWithFinalizer : BaseClassWithFinalizer
1+
public class DerivedClassWithFinalizer : BaseClassWithFinalizer
22
{
33
// To detect redundant calls
44
private bool _disposedValue;
55

6-
~DerivedClassWithFinalizer() => this.Dispose(false);
6+
~DerivedClassWithFinalizer() => Dispose(false);
77

88
// Protected implementation of Dispose pattern.
99
protected override void Dispose(bool disposing)
Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
Imports Microsoft.Win32.SafeHandles
22
Imports System.Runtime.InteropServices
33

4-
Class BaseClassWithSafeHandle : Implements IDisposable
5-
' Flag: Has Dispose already been called?
6-
Dim disposed As Boolean = False
4+
Public Class BaseClassWithSafeHandle
5+
Implements IDisposable
6+
7+
' To detect redundant calls
8+
Private _disposedValue As Boolean
9+
710
' Instantiate a SafeHandle instance.
8-
Dim handle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)
11+
Private _safeHandle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)
912

1013
' Public implementation of Dispose pattern callable by consumers.
1114
Public Sub Dispose() _
@@ -15,13 +18,15 @@ Class BaseClassWithSafeHandle : Implements IDisposable
1518
End Sub
1619

1720
' Protected implementation of Dispose pattern.
18-
Protected Overridable Sub Dispose(disposing As Boolean)
19-
If disposed Then Return
21+
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
22+
If Not _disposedValue Then
2023

21-
If disposing Then
22-
handle.Dispose()
23-
End If
24+
If disposing Then
25+
_safeHandle?.Dispose()
26+
_safeHandle = Nothing
27+
End If
2428

25-
disposed = True
29+
_disposedValue = True
30+
End If
2631
End Sub
2732
End Class
Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
Class BaseClassWithFinalizer : Implements IDisposable
2-
' Flag: Has Dispose already been called?
3-
Dim disposed As Boolean = False
1+
Public Class BaseClassWithFinalizer
2+
Implements IDisposable
3+
4+
' To detect redundant calls
5+
Private _disposedValue As Boolean
6+
7+
Protected Overrides Sub Finalize()
8+
Dispose(False)
9+
End Sub
410

511
' Public implementation of Dispose pattern callable by consumers.
612
Public Sub Dispose() _
@@ -10,20 +16,16 @@
1016
End Sub
1117

1218
' Protected implementation of Dispose pattern.
13-
Protected Overridable Sub Dispose(disposing As Boolean)
14-
If disposed Then Return
19+
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
20+
If Not _disposedValue Then
1521

16-
If disposing Then
17-
' Dispose managed objects that implement IDisposable.
18-
' Assign null to managed objects that consume large amounts of memory or consume scarce resources.
19-
End If
22+
If disposing Then
23+
' TODO: dispose managed state (managed objects)
24+
End If
2025

21-
' Free any unmanaged objects here.
22-
'
23-
disposed = True
24-
End Sub
25-
26-
Protected Overrides Sub Finalize()
27-
Dispose(False)
26+
' TODO free unmanaged resources (unmanaged objects) And override finalizer
27+
' TODO: set large fields to null
28+
_disposedValue = True
29+
End If
2830
End Sub
2931
End Class

samples/snippets/visualbasic/VS_Snippets_CLR_System/system.idisposable/vb/derived1.vb

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
Imports Microsoft.Win32.SafeHandles
22
Imports System.Runtime.InteropServices
33

4-
Class DerivedClassWithSafeHandle : Inherits BaseClassWithSafeHandle
5-
' Flag: Has Dispose already been called?
6-
Dim disposed As Boolean = False
4+
Public Class DerivedClassWithSafeHandle
5+
Inherits BaseClassWithSafeHandle
6+
7+
' To detect redundant calls
8+
Private _disposedValue As Boolean
9+
710
' Instantiate a SafeHandle instance.
8-
Dim handle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)
11+
Private _safeHandle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)
912

10-
' Protected implementation of Dispose pattern.
11-
Protected Overrides Sub Dispose(disposing As Boolean)
12-
If disposed Then Return
13+
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
14+
If Not _disposedValue Then
1315

14-
If disposing Then
15-
handle.Dispose()
16-
' Free any other managed objects here.
17-
'
18-
End If
16+
If disposing Then
17+
_safeHandle?.Dispose()
18+
_safeHandle = Nothing
19+
End If
1920

20-
' Free any unmanaged objects here.
21-
'
22-
disposed = True
21+
_disposedValue = True
22+
End If
2323

2424
' Call base class implementation.
2525
MyBase.Dispose(disposing)

0 commit comments

Comments
 (0)