GenerateDispose 1.0.1

dotnet add package GenerateDispose --version 1.0.1
                    
NuGet\Install-Package GenerateDispose -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GenerateDispose" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GenerateDispose" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="GenerateDispose" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add GenerateDispose --version 1.0.1
                    
#r "nuget: GenerateDispose, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package GenerateDispose@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=GenerateDispose&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=GenerateDispose&version=1.0.1
                    
Install as a Cake Tool

GenerateDispose for C#

GenerateDispose logo

Project Description

GenerateDispose is a Rosyln-powered generator for the Dispose pattern's boilerplate code.

Motivation

The purpose of this package is to allow C# developers to replace over 10 lines of boilerplate code with a single attribute when implementing the Dispose pattern, and also make the implemented pattern accommodate itself to future changes in the class modifiers.

Example

Suppose you have a class like the following:

public sealed class StreamOfConsciousness : IDisposable
{
    // some field declarations

    // some more stuff
}

The IDE would show a CS0535 error on IDisposable. Selecting Implement interface with Dispose pattern from the Show potential fixes menu would change the class into something like:

public sealed class StreamOfConsciousness : IDisposable
{
    // some field declarations
    private bool _disposedValue;

    // some more stuff

    private void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects)
            }

            _disposedValue = true;
        }
    }

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

(I took the liberty of clearing the snippet above from all comments not directly related to managed state.)

As important as the Dispose pattern may be, it is a textbook example of boilerplate code - the same huge envelope is "stamped" into your code, and you just paste the disposing logic (usually one or two lines) that is the reason you wrote : IDisposable in the first place.

But bloating up your code is not the end of it.

If the original class was not declared as sealed, the generated pattern would be exactly the same, except that Dispose(bool disposing) would now be protected virtual instead of private. (That stands to reason because now your class can be extended by other child classes and they might have Dispose methods of their own, which must also be called.)

So, if you start out with a sealed class having the Dispose pattern and then, at some point in the future, you want to make it non-sealed (for example if you want to expose it to external users but find out that keeping it sealed would break the Open/Closed Principle), you would also have to remember to fiddle around with the Dispose pattern that was generated at some point in the past, and who can remember.

Moreover, if (after adding the Dispose-pattern boilerplate code) you replace IDisposable with MemoryStream for example (because you just realized that it fits your purposes better) you would start getting compiler messages and have to resolve them all:

  1. First you would get a CS0108 warning on the Dispose() method (because it now hides the same method in MemoryStream) and a CA1816 warning on the GC.SuppressFinalize(this) call inside it. To resolve that you would have to manually remove the Dispose() method entirely.
  2. You would also get a CS0114 warning on the Dispose(bool disposing) method because now it, too, hides the same method inherited from MemoryStream. To resolve that you would have to manually make this method protected override.
  3. Having done that, you would now get a CA2215 warning on the same method. To resolve that you would have to manually add a base.Dispose(disposing); call to the end of this method.

That is a lot of redundant manual work for simply maintaining the boilerplate code that takes up room in your codebase anyway.

And that is where this source-generator comes in. Consider the following rephrase:

[GenerateDispose(nameof(DisposeImpl))]
public sealed partial class StreamOfConsciousness : IDisposable
{
    // some field declarations

    private void DisposeImpl()
    {
        // TODO: dispose managed state (managed objects)
    }

    // some more stuff
}

This is almost identical to the original snippet, except a method that handles the disposal has been added (I called it DisposeImpl but you can call it however you want) and a GenerateDispose attribute has been added to the class (now also made partial), with the nameof that disposal-method as its only argument.

And that's it. All the boilerplate code is generated and handled out of view, and accommodates itself to changes such as sealed changes or IDisposable parent classes. (You can see that even the : IDisposable suffix of the class declaration line is not necessary anymore.) You end up with code that is much more focused on your core business, with a lot less clutter. Just make sure the disposing method (DisposeImpl in this example) is one that can be called without providing any arguments, and you're good.

If you want to see the members generated for the currently edited class – namely two public void Dispose methods and a private int _isDisposed field – you can find them (as grayed-out) in the member drop-down list of the navigation bar.

Requirements

SDK: .NET 10.0 and up. (Use dotnet --version, dotnet --info or any other way to retrieve the .NET version you have installed.)

Install and Setup

To use GenerateDispose, include its package in your C# project by either following the package installation command (for your tool of choice) in the GenerateDispose NuGet package page, or searching for (and installing) it via the NuGet Package Manager.

It is recommended you manage your packages centrally, either manually or by following the package installation command for CPM in the NuGet package page. If you do that, you can make GenerateDispose available in all projects of your solution by adding the following to an ItemGroup element in your Directory.Build.props file instead of in each project separately:

<PackageReference Include="GenerateDispose" PrivateAssets="all" IncludeAssets="analyzers" />

It is then also recommended you add the following to your Directory.Build.props file:

<ItemGroup>
  <Using Include="GenerateDispose.SourceGenerators" />
</ItemGroup>

Acknowledgement

Special thanks go to Yehuda Arkin Adar without whose experience, professional advice and reviews this generator could not have been implemented with the quality it has today.

The logo of this project was designed by the very skilled @_duck_pie.

Happy coding!

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 102 5/2/2026
1.0.0 1,044 4/25/2026

## 1.0.1:

Non-Functional:
# Improve README.md

Bug fixes:
# Fully qualify all System classes in generated code

## 1.0.0:

Initial release