Skip to content

Commit d708831

Browse files
deep-outcomeJiří
andauthored
Implement dispose method fixes (#26192)
* NotOverridable instead of NonInheritable * Some clarification on object resource freeing up * Dubious statement removal * Clarification of Dispose(), Dispose(bool) introduction * Csprojs fix * Cascade dispose calls Foo simplification * Double blank line removal * ImplementTheDisposePattern fixes * ImplementTheDisposePatternForADerivedClass fixes * Duplicate examples removal * File examples removal Co-authored-by: Jiří <[email protected]>
1 parent bd1afb5 commit d708831

File tree

15 files changed

+59
-316
lines changed

15 files changed

+59
-316
lines changed

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

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,16 @@ The following derived classes in the <xref:Microsoft.Win32.SafeHandles> namespac
4141

4242
## Dispose() and Dispose(bool)
4343

44-
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 to be implemented:
44+
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.
4545

46-
- A `public` non-virtual (`NonInheritable` in Visual Basic) <xref:System.IDisposable.Dispose%2A?displayProperty=nameWithType> implementation that has no parameters.
47-
- A `protected virtual` (`Overridable` in Visual Basic) `Dispose` method whose signature is:
46+
Method signatures are:
4847

49-
:::code language="csharp" source="../../../samples/snippets/csharp/VS_Snippets_CLR/conceptual.disposable/cs/Disposable.cs" id="DisposeBool":::
50-
:::code language="vb" source="../../../samples/snippets/visualbasic/VS_Snippets_CLR/conceptual.disposable/vb/Disposable.vb" id="DisposeBool":::
51-
52-
> [!IMPORTANT]
53-
> The `disposing` parameter should be `false` when called from a finalizer, and `true` when called from the <xref:System.IDisposable.Dispose%2A?displayProperty=nameWithType> method. In other words, it is `true` when deterministically called and `false` when non-deterministically called.
48+
- `public` non-virtual (`NotOverridable` in Visual Basic) (<xref:System.IDisposable.Dispose%2A?displayProperty=nameWithType> implementation).
49+
- `protected virtual` (`Overridable` in Visual Basic) `Dispose(bool)`.
5450

5551
### The Dispose() method
5652

57-
Because the `public`, non-virtual (`NonInheritable` in Visual Basic), parameterless `Dispose` method is called 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:
53+
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:
5854

5955
:::code language="csharp" source="../../../samples/snippets/csharp/VS_Snippets_CLR/conceptual.disposable/cs/Disposable.cs" id="Dispose":::
6056
:::code language="vb" source="../../../samples/snippets/visualbasic/VS_Snippets_CLR/conceptual.disposable/vb/Disposable.vb" id="Dispose":::
@@ -65,14 +61,21 @@ The `Dispose` method performs all object cleanup, so the garbage collector no lo
6561

6662
In the overload, the `disposing` parameter is a <xref:System.Boolean> that indicates whether the method call comes from a <xref:System.IDisposable.Dispose%2A> method (its value is `true`) or from a finalizer (its value is `false`).
6763

68-
The body of the method consists of two blocks of code:
64+
:::code language="csharp" source="../../../samples/snippets/csharp/VS_Snippets_CLR/conceptual.disposable/cs/Disposable.cs" id="DisposeBool":::
65+
:::code language="vb" source="../../../samples/snippets/visualbasic/VS_Snippets_CLR/conceptual.disposable/vb/Disposable.vb" id="DisposeBool":::
66+
67+
> [!IMPORTANT]
68+
> The `disposing` parameter should be `false` when called from a finalizer, and `true` when called from the <xref:System.IDisposable.Dispose%2A?displayProperty=nameWithType> method. In other words, it is `true` when deterministically called and `false` when non-deterministically called.
69+
70+
The body of the method consists of three blocks of code:
6971

72+
- A block for conditional return if object is already disposed.
7073
- A block that frees unmanaged resources. This block executes regardless of the value of the `disposing` parameter.
7174
- A conditional block that frees managed resources. This block executes if the value of `disposing` is `true`. The managed resources that it frees can include:
7275

7376
- **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.
7477

75-
- **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, and this is usually done outside of the conditional block.
78+
- **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.
7679

7780
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 reclaimed. This is important because the order in which the garbage collector destroys managed objects during finalization is non-deterministic.
7881

@@ -94,7 +97,7 @@ All non-sealed classes or (Visual Basic classes not modified as `NotInheritable`
9497
> [!IMPORTANT]
9598
> It is 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.
9699
97-
Here's the general pattern for implementing the dispose pattern for a base class that uses a safe handle.
100+
Here's an example (of general pattern) for implementing the dispose pattern for a base class that uses a safe handle.
98101

99102
:::code language="csharp" source="../../../samples/snippets/csharp/VS_Snippets_CLR_System/system.idisposable/cs/base1.cs":::
100103
:::code language="vb" source="../../../samples/snippets/visualbasic/VS_Snippets_CLR_System/system.idisposable/vb/base1.vb":::
@@ -114,10 +117,10 @@ Here's the general pattern for implementing the dispose pattern for a base class
114117

115118
A class derived from a class that implements the <xref:System.IDisposable> interface shouldn't implement <xref:System.IDisposable>, because the base class implementation of <xref:System.IDisposable.Dispose%2A?displayProperty=nameWithType> is inherited by its derived classes. Instead, to cleanup a derived class, you provide the following:
116119

117-
- A `protected override void Dispose(bool)` method that overrides the base class method and performs the actual cleanup of the derived class. This method must also call the `base.Dispose(bool)` (`MyBase.Dispose(bool)` in Visual Basic) method of the base class and pass its disposing status for the argument.
118-
- Either a class derived from <xref:System.Runtime.InteropServices.SafeHandle> that wraps your unmanaged resource (recommended), or an override to the <xref:System.Object.Finalize%2A?displayProperty=nameWithType> method. The <xref:System.Runtime.InteropServices.SafeHandle> class provides a finalizer that frees you from having to code one. If you do provide a finalizer, it must call the `Dispose(bool)` overload with a `disposing` argument of `false`.
120+
- A `protected override void Dispose(bool)` method that overrides the base class method and performs the actual cleanup of the derived class. This method must also call the `base.Dispose(bool)` (`MyBase.Dispose(bool)` in Visual Basic) method passing it the disposing status (`bool disposing` parameter) as an argument.
121+
- Either a class derived from <xref:System.Runtime.InteropServices.SafeHandle> that wraps your unmanaged resource (recommended), or an override to the <xref:System.Object.Finalize%2A?displayProperty=nameWithType> method. The <xref:System.Runtime.InteropServices.SafeHandle> class provides a finalizer that frees you from having to code one. If you do provide a finalizer, it must call the `Dispose(bool)` overload with `false` argument.
119122

120-
Here's the general pattern for implementing the dispose pattern for a derived class that uses a safe handle:
123+
Here's an example (of general pattern) for implementing the dispose pattern for a derived class that uses a safe handle:
121124

122125
:::code language="csharp" source="../../../samples/snippets/csharp/VS_Snippets_CLR_System/system.idisposable/cs/derived1.cs":::
123126
:::code language="vb" source="../../../samples/snippets/visualbasic/VS_Snippets_CLR_System/system.idisposable/vb/derived1.vb":::
@@ -130,20 +133,6 @@ Here's the general pattern for implementing the dispose pattern for a derived cl
130133
:::code language="csharp" source="../../../samples/snippets/csharp/VS_Snippets_CLR_System/system.idisposable/cs/derived2.cs":::
131134
:::code language="vb" source="../../../samples/snippets/visualbasic/VS_Snippets_CLR_System/system.idisposable/vb/derived2.vb":::
132135

133-
## Implement the dispose pattern with safe handles
134-
135-
The following example illustrates the dispose pattern for a base class, `DisposableStreamResource`, that uses a safe handle to encapsulate unmanaged resources. It defines a `DisposableStreamResource` class that uses a <xref:Microsoft.Win32.SafeHandles.SafeFileHandle> to wrap a <xref:System.IO.Stream> object that represents an open file. The class also includes a single property, `Size`, that returns the total number of bytes in the file stream.
136-
137-
:::code language="csharp" source="../../../samples/snippets/csharp/VS_Snippets_CLR/conceptual.disposable/cs/DisposableStreamResource.cs":::
138-
:::code language="vb" source="../../../samples/snippets/visualbasic/VS_Snippets_CLR/conceptual.disposable/vb/DisposableStreamResource.vb":::
139-
140-
## Implement the dispose pattern for a derived class with safe handles
141-
142-
The following example illustrates the dispose pattern for a derived class, `DisposableStreamResource2`, that inherits from the `DisposableStreamResource` class presented in the previous example. The class adds an additional method, `WriteFileInfo`, and uses a <xref:Microsoft.Win32.SafeHandles.SafeFileHandle> object to wrap the handle of the writable file.
143-
144-
:::code language="csharp" source="../../../samples/snippets/csharp/VS_Snippets_CLR/conceptual.disposable/cs/DisposableStreamResource2.cs":::
145-
:::code language="vb" source="../../../samples/snippets/visualbasic/VS_Snippets_CLR/conceptual.disposable/vb/DisposableStreamResource2.vb":::
146-
147136
## See also
148137

149138
- [Disposal of services](../../core/extensions/dependency-injection-guidelines.md#disposal-of-services)

samples/snippets/csharp/VS_Snippets_CLR/conceptual.disposable/cs/Disposable.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
public class Disposable : IDisposable
44
{
5+
private bool _disposed;
56
// <SnippetDispose>
67
public void Dispose()
78
{
@@ -15,6 +16,18 @@ public void Dispose()
1516
// <SnippetDisposeBool>
1617
protected virtual void Dispose(bool disposing)
1718
{
19+
if (_disposed)
20+
return;
21+
22+
// A block that frees unmanaged resources.
23+
24+
if(disposing)
25+
{
26+
// Deterministic call…
27+
// A conditional block that frees managed resources.
28+
}
29+
30+
_disposed = true;
1831
}
1932
// </SnippetDisposeBool>
2033
}

samples/snippets/csharp/VS_Snippets_CLR/conceptual.disposable/cs/DisposableStreamResource.cs

Lines changed: 0 additions & 79 deletions
This file was deleted.

samples/snippets/csharp/VS_Snippets_CLR/conceptual.disposable/cs/DisposableStreamResource2.cs

Lines changed: 0 additions & 63 deletions
This file was deleted.

samples/snippets/csharp/VS_Snippets_CLR/conceptual.disposable/cs/Foo.cs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,7 @@
22

33
public sealed class Foo : IDisposable
44
{
5-
private readonly IDisposable _bar;
6-
7-
public Foo()
8-
{
9-
_bar = new Bar();
10-
}
11-
12-
public void Dispose()
13-
{
14-
_bar?.Dispose();
15-
}
5+
// Model simplified for brevity sake.
6+
private readonly IDisposable _bar = new Bar();
7+
public void Dispose() => _bar.Dispose();
168
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ protected virtual void Dispose(bool disposing)
2323

2424
if (disposing)
2525
{
26-
// Dispose managed state (managed objects).
27-
_safeHandle?.Dispose();
26+
_safeHandle.Dispose();
2827
}
2928

3029
_disposed = true;

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ protected virtual void Dispose(bool disposing)
2424

2525
if (disposing)
2626
{
27-
// TODO: dispose managed state (managed objects).
27+
// Dispose managed objects that implement IDisposable.
28+
// Assign null to managed objects that consume large amounts of memory or consume scarce resources.
2829
}
2930

30-
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
31-
// TODO: set large fields to null.
31+
// Free unmanaged resources (unmanaged objects).
3232

3333
_disposed = true;
3434
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ protected override void Dispose(bool disposing)
2020

2121
if (disposing)
2222
{
23-
// Dispose managed state (managed objects).
24-
_safeHandle?.Dispose();
23+
_safeHandle.Dispose();
2524
}
2625

2726
_disposed = true;

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ protected override void Dispose(bool disposing)
1515

1616
if (disposing)
1717
{
18-
// TODO: dispose managed state (managed objects).
18+
// Dispose managed objects that implement IDisposable.
19+
// Assign null to managed objects that consume large amounts of memory or consume scarce resources.
1920
}
2021

21-
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
22-
// TODO: set large fields to null.
22+
// Free unmanaged resources (unmanaged objects).
2323
_disposed = true;
2424

2525
// Call the base class implementation.

samples/snippets/visualbasic/VS_Snippets_CLR/conceptual.disposable/vb/Disposable.vb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
Public Class Disposable : Implements IDisposable
22

3+
Dim disposed As Boolean
34
' <SnippetDispose>
45
Public Sub Dispose() _
56
Implements IDisposable.Dispose
@@ -11,8 +12,18 @@
1112
' </SnippetDispose>
1213

1314
' <SnippetDisposeBool>
14-
Protected Overridable Sub Dispose(disposing As Boolean)
15-
End Sub
15+
Protected Overridable Sub Dispose(disposing As Boolean)
16+
If disposed Then Exit Sub
17+
18+
' A block that frees unmanaged resources.
19+
20+
If disposing Then
21+
' Deterministic call…
22+
' A conditional block that frees managed resources.
23+
End If
24+
25+
disposed = True
26+
End Sub
1627
' </SnippetDisposeBool>
1728

1829
End Class

0 commit comments

Comments
 (0)