Reinstate dispose pattern guidance (#33326)

This commit is contained in:
Genevieve Warren 2023-01-05 08:52:45 -08:00 коммит произвёл GitHub
Родитель 2b73976c20
Коммит 664c5a3415
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 297 добавлений и 16 удалений

Просмотреть файл

@ -375,10 +375,6 @@
"source_path_from_root": "/docs/standard/design-guidelines/choosing-between-anonymous-and-tuple.md",
"redirect_url": "/dotnet/standard/base-types/choosing-between-anonymous-and-tuple"
},
{
"source_path_from_root": "/docs/standard/design-guidelines/dispose-pattern.md",
"redirect_url": "/dotnet/standard/garbage-collection/implementing-dispose"
},
{
"source_path_from_root": "/docs/standard/events/how-to-consume-events-in-a-web-forms-application.md",
"redirect_url": "/aspnet/web-forms/overview/how-to-consume-events"

Просмотреть файл

@ -36,13 +36,13 @@ The <xref:System.IDisposable?displayProperty=nameWithType> interface is not impl
Violation of any one of these patterns triggers warning CA1063.
Every unsealed type that declares and implements the <xref:System.IDisposable> interface must provide its own `protected virtual void Dispose(bool)` method. `Dispose()` should call `Dispose(true)`, and the finalizer should call `Dispose(false)`. If you create an unsealed type that declares and implements the <xref:System.IDisposable> interface, you must define `Dispose(bool)` and call it. For more information, see [Clean up unmanaged resources (.NET guide)](../../../standard/garbage-collection/unmanaged.md) and [Dispose pattern](../../../standard/garbage-collection/implementing-dispose.md).
Every unsealed type that declares and implements the <xref:System.IDisposable> interface must provide its own `protected virtual void Dispose(bool)` method. `Dispose()` should call `Dispose(true)`, and the finalizer should call `Dispose(false)`. If you create an unsealed type that declares and implements the <xref:System.IDisposable> interface, you must define `Dispose(bool)` and call it. For more information, see [Clean up unmanaged resources (.NET guide)](../../../standard/garbage-collection/unmanaged.md) and [Implement a Dispose method](../../../standard/garbage-collection/implementing-dispose.md).
By default, this rule only looks at externally visible types, but this is [configurable](#configure-code-to-analyze).
## Rule description
All <xref:System.IDisposable> types should implement the [Dispose pattern](../../../standard/garbage-collection/implementing-dispose.md) correctly.
All <xref:System.IDisposable> types should [implement the Dispose pattern](../../../standard/garbage-collection/implementing-dispose.md) correctly.
## How to fix violations
@ -141,5 +141,5 @@ public class Resource : IDisposable
## See also
- [Dispose pattern (framework design guidelines)](../../../standard/garbage-collection/implementing-dispose.md)
- [Dispose pattern (framework design guidelines)](../../../standard/design-guidelines/dispose-pattern.md)
- [Clean up unmanaged resources (.NET guide)](../../../standard/garbage-collection/unmanaged.md)

Просмотреть файл

@ -104,4 +104,4 @@ This example shows a method that correctly calls <xref:System.GC.SuppressFinaliz
## See also
- [Dispose pattern](../../../standard/garbage-collection/implementing-dispose.md)
- [Dispose pattern](../../../standard/design-guidelines/dispose-pattern.md)

Просмотреть файл

@ -207,11 +207,11 @@ By default, the Visual Basic compiler has all arithmetic operators check for ove
To fix this, you can disable the emitting of overflow checks by the Visual Basic compiler in your project or you can modify your code as in the following CreateReader2 function.
To disable the emitting of overflow checks, right-click the project name in Solution Explorer and then click **Properties**. Click **Compile**, click **Advanced Compile Options**, and then check **Remove integer overflow checks**.
To disable the emitting of overflow checks, right-click the project name in Solution Explorer and then select **Properties**. Select **Compile** > **Advanced Compile Options**, and then check **Remove integer overflow checks**.
:::code language="vb" source="snippets/vb/all-rules/ca2000-dispose-objects-before-losing-scope-vboverflow_1.vb":::
## See also
- <xref:System.IDisposable>
- [Dispose Pattern](../../../standard/garbage-collection/implementing-dispose.md)
- [Dispose Pattern](../../../standard/design-guidelines/dispose-pattern.md)

Просмотреть файл

@ -114,4 +114,4 @@ protected virtual void Dispose(bool disposing)
## See also
- <xref:System.IDisposable?displayProperty=fullName>
- [Dispose pattern](../../../standard/garbage-collection/implementing-dispose.md)
- [Dispose pattern](../../../standard/design-guidelines/dispose-pattern.md)

Просмотреть файл

@ -77,4 +77,4 @@ The following example shows two types, `TypeA` that implements <xref:System.IDis
## See also
- <xref:System.IDisposable?displayProperty=fullName>
- [Dispose Pattern](../../../standard/garbage-collection/implementing-dispose.md)
- [Dispose Pattern](../../../standard/design-guidelines/dispose-pattern.md)

Просмотреть файл

@ -85,4 +85,4 @@ The following example shows a type that violates this rule.
- <xref:System.Runtime.InteropServices.HandleRef?displayProperty=fullName>
- <xref:System.UIntPtr?displayProperty=fullName>
- <xref:System.Object.Finalize%2A?displayProperty=fullName>
- [Dispose Pattern](../../../standard/garbage-collection/implementing-dispose.md)
- [Dispose Pattern](../../../standard/design-guidelines/dispose-pattern.md)

Просмотреть файл

@ -3193,6 +3193,8 @@ items:
items:
- name: Dependency properties
href: ../standard/design-guidelines/dependency-properties.md
- name: Dispose pattern
href: ../standard/design-guidelines/dispose-pattern.md
- name: Migration guide
items:
- name: Overview

Просмотреть файл

@ -14,7 +14,8 @@ There are numerous books on software patterns, pattern languages, and antipatter
## In This Section
[Dependency Properties](dependency-properties.md)
[Dispose Pattern](../garbage-collection/implementing-dispose.md)
[Dispose Pattern](dispose-pattern.md)
*Portions © 2005, 2009 Microsoft Corporation. All rights reserved.*
*Reprinted by permission of Pearson Education, Inc. from [Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, 2nd Edition](https://www.informit.com/store/framework-design-guidelines-conventions-idioms-and-9780321545619) by Krzysztof Cwalina and Brad Abrams, published Oct 22, 2008 by Addison-Wesley Professional as part of the Microsoft Windows Development Series.*

Просмотреть файл

@ -0,0 +1,282 @@
---
title: "Dispose Pattern"
ms.date: "10/22/2008"
ms.technology: dotnet-standard
helpviewer_keywords:
- "Dispose method"
- "class library design guidelines [.NET Framework], Dispose method"
- "class library design guidelines [.NET Framework], Finalize method"
- "customizing Dispose method name"
- "Finalize method"
ms.assetid: 31a6c13b-d6a2-492b-9a9f-e5238c983bcb
author: "KrzysztofCwalina"
---
# Dispose Pattern
All programs acquire one or more system resources, such as memory, system handles, or database connections, during the course of their execution. Developers have to be careful when using such system resources, because they must be released after they have been acquired and used.
The CLR provides support for automatic memory management. Managed memory (memory allocated using the C# operator `new`) does not need to be explicitly released. It is released automatically by the garbage collector (GC). This frees developers from the tedious and difficult task of releasing memory and has been one of the main reasons for the unprecedented productivity afforded by the .NET Framework.
Unfortunately, managed memory is just one of many types of system resources. Resources other than managed memory still need to be released explicitly and are referred to as unmanaged resources. The GC was specifically not designed to manage such unmanaged resources, which means that the responsibility for managing unmanaged resources lies in the hands of the developers.
The CLR provides some help in releasing unmanaged resources. <xref:System.Object?displayProperty=nameWithType> declares a virtual method <xref:System.Object.Finalize%2A> (also called the finalizer) that is called by the GC before the objects memory is reclaimed by the GC and can be overridden to release unmanaged resources. Types that override the finalizer are referred to as finalizable types.
Although finalizers are effective in some cleanup scenarios, they have two significant drawbacks:
- The finalizer is called when the GC detects that an object is eligible for collection. This happens at some undetermined period of time after the resource is not needed anymore. The delay between when the developer could or would like to release the resource and the time when the resource is actually released by the finalizer might be unacceptable in programs that acquire many scarce resources (resources that can be easily exhausted) or in cases in which resources are costly to keep in use (e.g., large unmanaged memory buffers).
- When the CLR needs to call a finalizer, it must postpone collection of the objects memory until the next round of garbage collection (the finalizers run between collections). This means that the objects memory (and all objects it refers to) will not be released for a longer period of time.
Therefore, relying exclusively on finalizers might not be appropriate in many scenarios when it is important to reclaim unmanaged resources as quickly as possible, when dealing with scarce resources, or in highly performant scenarios in which the added GC overhead of finalization is unacceptable.
The Framework provides the <xref:System.IDisposable?displayProperty=nameWithType> interface that should be implemented to provide the developer a manual way to release unmanaged resources as soon as they are not needed. It also provides the <xref:System.GC.SuppressFinalize%2A?displayProperty=nameWithType> method that can tell the GC that an object was manually disposed of and does not need to be finalized anymore, in which case the objects memory can be reclaimed earlier. Types that implement the `IDisposable` interface are referred to as disposable types.
The Dispose Pattern is intended to standardize the usage and implementation of finalizers and the `IDisposable` interface.
The main motivation for the pattern is to reduce the complexity of the implementation of the <xref:System.Object.Finalize%2A> and the <xref:System.IDisposable.Dispose%2A> methods. The complexity stems from the fact that the methods share some but not all code paths (the differences are described later in the chapter). In addition, there are historical reasons for some elements of the pattern related to the evolution of language support for deterministic resource management.
**✓ DO** implement the Basic Dispose Pattern on types containing instances of disposable types. See the [Basic Dispose Pattern](#basic-dispose-pattern) section for details on the basic pattern.
If a type is responsible for the lifetime of other disposable objects, developers need a way to dispose of them, too. Using the containers `Dispose` method is a convenient way to make this possible.
**✓ DO** implement the Basic Dispose Pattern and provide a finalizer on types holding resources that need to be freed explicitly and that do not have finalizers.
For example, the pattern should be implemented on types storing unmanaged memory buffers. The [Finalizable Types](#finalizable-types) section discusses guidelines related to implementing finalizers.
**✓ CONSIDER** implementing the Basic Dispose Pattern on classes that themselves dont hold unmanaged resources or disposable objects but are likely to have subtypes that do.
A great example of this is the <xref:System.IO.Stream?displayProperty=nameWithType> class. Although it is an abstract base class that doesnt hold any resources, most of its subclasses do and because of this, it implements this pattern.
## Basic Dispose Pattern
The basic implementation of the pattern involves implementing the `System.IDisposable` interface and declaring the `Dispose(bool)` method that implements all resource cleanup logic to be shared between the `Dispose` method and the optional finalizer.
The following example shows a simple implementation of the basic pattern:
```csharp
public class DisposableResourceHolder : IDisposable {
private SafeHandle resource; // handle to a resource
public DisposableResourceHolder() {
this.resource = ... // allocates the resource
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (disposing) {
if (resource!= null) resource.Dispose();
}
}
}
```
The Boolean parameter `disposing` indicates whether the method was invoked from the `IDisposable.Dispose` implementation or from the finalizer. The `Dispose(bool)` implementation should check the parameter before accessing other reference objects (e.g., the resource field in the preceding sample). Such objects should only be accessed when the method is called from the `IDisposable.Dispose` implementation (when the `disposing` parameter is equal to true). If the method is invoked from the finalizer (`disposing` is false), other objects should not be accessed. The reason is that objects are finalized in an unpredictable order and so they, or any of their dependencies, might already have been finalized.
Also, this section applies to classes with a base that does not already implement the Dispose Pattern. If you are inheriting from a class that already implements the pattern, simply override the `Dispose(bool)` method to provide additional resource cleanup logic.
**✓ DO** declare a `protected virtual void Dispose(bool disposing)` method to centralize all logic related to releasing unmanaged resources.
All resource cleanup should occur in this method. The method is called from both the finalizer and the `IDisposable.Dispose` method. The parameter will be false if being invoked from inside a finalizer. It should be used to ensure any code running during finalization is not accessing other finalizable objects. Details of implementing finalizers are described in the next section.
```csharp
protected virtual void Dispose(bool disposing) {
if (disposing) {
if (resource!= null) resource.Dispose();
}
}
```
**✓ DO** implement the `IDisposable` interface by simply calling `Dispose(true)` followed by `GC.SuppressFinalize(this)`.
The call to `SuppressFinalize` should only occur if `Dispose(true)` executes successfully.
```csharp
public void Dispose(){
Dispose(true);
GC.SuppressFinalize(this);
}
```
**X DO NOT** make the parameterless `Dispose` method virtual.
The `Dispose(bool)` method is the one that should be overridden by subclasses.
```csharp
// bad design
public class DisposableResourceHolder : IDisposable {
public virtual void Dispose() { ... }
protected virtual void Dispose(bool disposing) { ... }
}
// good design
public class DisposableResourceHolder : IDisposable {
public void Dispose() { ... }
protected virtual void Dispose(bool disposing) { ... }
}
```
**X DO NOT** declare any overloads of the `Dispose` method other than `Dispose()` and `Dispose(bool)`.
`Dispose` should be considered a reserved word to help codify this pattern and prevent confusion among implementers, users, and compilers. Some languages might choose to automatically implement this pattern on certain types.
**✓ DO** allow the `Dispose(bool)` method to be called more than once. The method might choose to do nothing after the first call.
```csharp
public class DisposableResourceHolder : IDisposable {
bool disposed = false;
protected virtual void Dispose(bool disposing) {
if (disposed) return;
// cleanup
...
disposed = true;
}
}
```
**X AVOID** throwing an exception from within `Dispose(bool)` except under critical situations where the containing process has been corrupted (leaks, inconsistent shared state, etc.).
Users expect that a call to `Dispose` will not raise an exception.
If `Dispose` could raise an exception, further finally-block cleanup logic will not execute. To work around this, the user would need to wrap every call to `Dispose` (within the finally block!) in a try block, which leads to very complex cleanup handlers. If executing a `Dispose(bool disposing)` method, never throw an exception if disposing is false. Doing so will terminate the process if executing inside a finalizer context.
**✓ DO** throw an <xref:System.ObjectDisposedException> from any member that cannot be used after the object has been disposed of.
```csharp
public class DisposableResourceHolder : IDisposable {
bool disposed = false;
SafeHandle resource; // handle to a resource
public void DoSomething() {
if (disposed) throw new ObjectDisposedException(...);
// now call some native methods using the resource
...
}
protected virtual void Dispose(bool disposing) {
if (disposed) return;
// cleanup
...
disposed = true;
}
}
```
**✓ CONSIDER** providing method `Close()`, in addition to the `Dispose()`, if close is standard terminology in the area.
When doing so, it is important that you make the `Close` implementation identical to `Dispose` and consider implementing the `IDisposable.Dispose` method explicitly.
```csharp
public class Stream : IDisposable {
IDisposable.Dispose() {
Close();
}
public void Close() {
Dispose(true);
GC.SuppressFinalize(this);
}
}
```
## Finalizable Types
Finalizable types are types that extend the Basic Dispose Pattern by overriding the finalizer and providing finalization code path in the `Dispose(bool)` method.
Finalizers are notoriously difficult to implement correctly, primarily because you cannot make certain (normally valid) assumptions about the state of the system during their execution. The following guidelines should be taken into careful consideration.
Note that some of the guidelines apply not just to the `Finalize` method, but to any code called from a finalizer. In the case of the Basic Dispose Pattern previously defined, this means logic that executes inside `Dispose(bool disposing)` when the `disposing` parameter is false.
If the base class already is finalizable and implements the Basic Dispose Pattern, you should not override `Finalize` again. You should instead just override the `Dispose(bool)` method to provide additional resource cleanup logic.
The following code shows an example of a finalizable type:
```csharp
public class ComplexResourceHolder : IDisposable {
private IntPtr buffer; // unmanaged memory buffer
private SafeHandle resource; // disposable handle to a resource
public ComplexResourceHolder() {
this.buffer = ... // allocates memory
this.resource = ... // allocates the resource
}
protected virtual void Dispose(bool disposing) {
ReleaseBuffer(buffer); // release unmanaged memory
if (disposing) { // release other disposable objects
if (resource!= null) resource.Dispose();
}
}
~ComplexResourceHolder() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
}
```
**X AVOID** making types finalizable.
Carefully consider any case in which you think a finalizer is needed. There is a real cost associated with instances with finalizers, from both a performance and code complexity standpoint. Prefer using resource wrappers such as <xref:System.Runtime.InteropServices.SafeHandle> to encapsulate unmanaged resources where possible, in which case a finalizer becomes unnecessary because the wrapper is responsible for its own resource cleanup.
**X DO NOT** make value types finalizable.
Only reference types actually get finalized by the CLR, and thus any attempt to place a finalizer on a value type will be ignored. The C# and C++ compilers enforce this rule.
**✓ DO** make a type finalizable if the type is responsible for releasing an unmanaged resource that does not have its own finalizer.
When implementing the finalizer, simply call `Dispose(false)` and place all resource cleanup logic inside the `Dispose(bool disposing)` method.
```csharp
public class ComplexResourceHolder : IDisposable {
~ComplexResourceHolder() {
Dispose(false);
}
protected virtual void Dispose(bool disposing) {
...
}
}
```
**✓ DO** implement the Basic Dispose Pattern on every finalizable type.
This gives users of the type a means to explicitly perform deterministic cleanup of those same resources for which the finalizer is responsible.
**X DO NOT** access any finalizable objects in the finalizer code path, because there is significant risk that they will have already been finalized.
For example, a finalizable object A that has a reference to another finalizable object B cannot reliably use B in As finalizer, or vice versa. Finalizers are called in a random order (short of a weak ordering guarantee for critical finalization).
Also, be aware that objects stored in static variables will get collected at certain points during an application domain unload or while exiting the process. Accessing a static variable that refers to a finalizable object (or calling a static method that might use values stored in static variables) might not be safe if <xref:System.Environment.HasShutdownStarted%2A?displayProperty=nameWithType> returns true.
**✓ DO** make your `Finalize` method protected.
C#, C++, and VB.NET developers do not need to worry about this, because the compilers help to enforce this guideline.
**X DO NOT** let exceptions escape from the finalizer logic, except for system-critical failures.
If an exception is thrown from a finalizer, the CLR will shut down the entire process (as of .NET Framework version 2.0), preventing other finalizers from executing and resources from being released in a controlled manner.
**✓ CONSIDER** creating and using a critical finalizable object (a type with a type hierarchy that contains <xref:System.Runtime.ConstrainedExecution.CriticalFinalizerObject>) for situations in which a finalizer absolutely must execute even in the face of forced application domain unloads and thread aborts.
*Portions © 2005, 2009 Microsoft Corporation. All rights reserved.*
*Reprinted by permission of Pearson Education, Inc. from [Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, 2nd Edition](https://www.informit.com/store/framework-design-guidelines-conventions-idioms-and-9780321545619) by Krzysztof Cwalina and Brad Abrams, published Oct 22, 2008 by Addison-Wesley Professional as part of the Microsoft Windows Development Series.*
## See also
- <xref:System.IDisposable.Dispose%2A?displayProperty=nameWithType>
- <xref:System.Object.Finalize%2A?displayProperty=nameWithType>
- [Framework Design Guidelines](../../../docs/standard/design-guidelines/index.md)
- [Common Design Patterns](../../../docs/standard/design-guidelines/common-design-patterns.md)
- [Garbage Collection](../../../docs/standard/garbage-collection/index.md)

Просмотреть файл

@ -38,7 +38,7 @@ This section provides guidelines for designing libraries that extend and interac
Describes guidelines for using common types such as arrays, attributes, and collections, supporting serialization, and overloading equality operators.
[Common Design Patterns](common-design-patterns.md)\
Provides guidelines for choosing and implementing dependency properties.
Provides guidelines for choosing and implementing dependency properties and the dispose pattern.
*Portions © 2005, 2009 Microsoft Corporation. All rights reserved.*

Просмотреть файл

@ -10,7 +10,7 @@ ms.assetid: 42215ffa-a099-4a26-b14e-fb2bdb6f95b7
This section contains guidelines for using common types in publicly accessible APIs. It deals with direct usage of built-in Framework types (e.g., serialization attributes) and overloading common operators.
The <xref:System.IDisposable?displayProperty=nameWithType> interface is not covered in this section, but is discussed in the [Dispose Pattern](../garbage-collection/implementing-dispose.md) section.
The <xref:System.IDisposable?displayProperty=nameWithType> interface is not covered in this section, but is discussed in the [Dispose Pattern](dispose-pattern.md) section.
> [!NOTE]
> For guidelines and additional information about other common, built-in .NET Framework types, see the reference topics for the following: <xref:System.DateTime?displayProperty=nameWithType>, <xref:System.DateTimeOffset?displayProperty=nameWithType>, <xref:System.ICloneable?displayProperty=nameWithType>, <xref:System.IComparable%601?displayProperty=nameWithType>, <xref:System.IEquatable%601?displayProperty=nameWithType>, <xref:System.Nullable%601?displayProperty=nameWithType>, <xref:System.Object?displayProperty=nameWithType>, <xref:System.Uri?displayProperty=nameWithType>.