Dirge 4.0.0

dotnet add package Dirge --version 4.0.0
                    
NuGet\Install-Package Dirge -Version 4.0.0
                    
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="Dirge" Version="4.0.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Dirge" Version="4.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Dirge">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
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 Dirge --version 4.0.0
                    
#r "nuget: Dirge, 4.0.0"
                    
#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 Dirge@4.0.0
                    
#: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=Dirge&version=4.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Dirge&version=4.0.0
                    
Install as a Cake Tool

Dirge

Test Version Download MIT License

Disposable Implementation Roslyn Generator Extension

Installation

You can install the EnumSerializer from NuGet.

Usage

Mark a class with the [AutoDispose] attribute and implement the IDisposable interface. The generator will automatically generate the implementation of the Dispose method for you.

using Dirge;

namespace Test;

[AutoDispose]
internal partial class TestClass
{
    private readonly Stream _stream = new MemoryStream();
}

The generated code will look like this:

namespace Test;

partial class TestClass : IDisposable
{
    private bool __generated_disposed = false;

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

    protected virtual void Dispose(bool disposing)
    {
        if (this.__generated_disposed) return;

        try
        {
            if (disposing)
            {
                this._stream?.Dispose();
            }
        }
        finally
        {
            this.__generated_disposed = true;
        }
    }
}

Note that this example is simplified for demonstration purposes.

To suppress the auto-generated Dispose call for a specific field, you can use the [DoNotDispose] attribute:

using Dirge;

namespace Test;

[AutoDispose]
internal partial class TestClass
{
    private readonly Stream _stream1 = new MemoryStream();

    [DoNotDispose]
    private readonly Stream _stream2 = new MemoryStream(); // This field will not be disposed by the generated Dispose method.
}

Ref-struct is also supported, but IDisposable will not be implemented regardless of the language version.

Conditional disposal

Conditional disposal, which allows you to specify conditions under which a field should be disposed, is also supported. You can use the [DoNotDisposeWhen] attribute with a boolean field and a value to compare against:

using Dirge;

namespace Test;

[AutoDispose]
internal partial class TestClass
{
    private readonly bool _leaveOpen;

    [DoNotDisposeWhen(nameof(_leaveOpen), true)]
    private readonly Stream _stream;

    internal TestClass(Stream stream, bool leaveOpen)
    {
        this._stream = stream;
        this._leaveOpen = leaveOpen;
    }
}

This will prevent the generator from disposing the _stream field when the _leaveOpen field is true:

namespace Test;

partial class TestClass : IDisposable
{
    private bool __generated_disposed = false;

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

    protected virtual void Dispose(bool disposing)
    {
        if (this.__generated_disposed) return;

        try
        {
            if (disposing)
            {
                if (!this._leaveOpen)
                {
                    this._stream?.Dispose();
                }
            }
        }
        finally
        {
            this.__generated_disposed = true;
        }
    }
}

Unmanaged resources

To safely release unmanaged resources, this generator also supports the implementation of a finalizer. You can specify a method to release unmanaged resources through the ReleaseUnmanagedResources option:

using Dirge;
using System.IO;
        
namespace Test;
        
[AutoDispose(ReleaseUnmanagedResources = nameof(ReleaseUnmanagedResources))]
internal sealed partial class TestClass
{
    private readonly Stream _stream;

    internal void ReleaseUnmanagedResources()
    {
        // Custom logic to release unmanaged resources
    }
}

This will generate a finalizer that calls the specified method to release unmanaged resources:

namespace Test;

sealed partial class TestClass : IDisposable
{
    private bool __generated_disposed = false;

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

    private void Dispose(bool disposing)
    {
        if (this.__generated_disposed) return;

        try
        {
            if (disposing)
            {
                this._stream?.Dispose();
            }

            ReleaseUnmanagedResources();
        }
        finally
        {
            this.__generated_disposed = true;
        }
    }

    ~TestClass()
    {
        Dispose(false);
    }
}

Asynchronous disposal

Use [AutoDispose(IncludeAsync = true)] on a partial class to generate both IDisposable and IAsyncDisposable. IncludeAsync defaults to false, so existing [AutoDispose] usage and explicit IncludeAsync = false retain synchronous-only generation and ignore fields that implement only IAsyncDisposable.

Specify all options, including ReleaseUnmanagedResources, on this single attribute. AllowMultiple = false prevents duplicate attributes, including duplicates across partial declarations; the compiler reports CS0579.

using Dirge;
using System.IO;

[AutoDispose(IncludeAsync = true)]
internal sealed partial class AsyncOwner
{
    private readonly Stream _stream = new MemoryStream();
}

// In an async method:
// await using var owner = new AsyncOwner();

DisposeAsync() awaits each resource with ConfigureAwait(false), preferring IAsyncDisposable and falling back to IDisposable. When a resource's type or generic constraints guarantee the interface, generated code calls it directly with any needed null check. Interface conversions are retained for explicit implementations or hidden methods; runtime interface checks are used when the static type cannot determine the disposal path. Dispose() calls only IDisposable; it does not block on asynchronous cleanup. Resources that support only asynchronous disposal therefore require DisposeAsync() / await using.

Field selection uses the input compilation: the field's declared type (including generic constraints and nullable value types) must implement IDisposable or IAsyncDisposable. Attributes on the resource type are not used to predict interfaces that might be generated later. Declare the interface on that type or use an interface-typed field to include such a resource. An object-typed field is ignored. Runtime checks only select an additional disposal interface on an already selected field, without casting through object. The existing synchronous ref-struct pattern-based exception is unchanged.

[DoNotDispose], [DoNotDisposeWhen], and ReleaseUnmanagedResources work with both paths. Static fields and null resources are skipped. Non-sealed classes expose protected virtual ValueTask DisposeAsyncCore(). After asynchronous cleanup, Dispose(false) releases unmanaged resources without disposing managed resources again, and finalization is suppressed. This follows the Microsoft async dispose pattern.

Sequential calls to either disposal method do not dispose fields again. If a resource throws, later fields in that method are skipped; generated base cleanup and unmanaged cleanup still run through finally blocks, and failed cleanup is not retried. Concurrent disposal is not synchronized.

IncludeAsync = true requires a framework that provides IAsyncDisposable and ValueTask. It supports classes, including generic and nested classes, but reports DIRGE007 for structs or ref structs. Synchronous struct support is unchanged when IncludeAsync is omitted or false. Disposal methods on the target class must be generated. Derived classes can inherit another [AutoDispose(IncludeAsync = true)] class, or a class implementing both interfaces with accessible, non-abstract, overridable Dispose(bool) and DisposeAsyncCore() methods. A base class with both public disposal entry points abstract is also supported. Other disposable base configurations produce DIRGE007 instead of silently hiding cleanup methods. Handwritten base classes must follow the same disposal pattern and govern their own exception and repeat-call behavior. When a derived class adds neither disposable fields nor ReleaseUnmanagedResources, and the base provides both implementations, no code is generated for the derived class. It inherits the base behavior unchanged. Root classes and classes implementing abstract disposal entry points still receive implementations even without resources.

Constraints

To generate the Dispose method, the class (or struct) must meet the following constraints:

  • It must be a non-static class.
  • It must be a partial class or struct.
  • It must not be a readonly struct.

For conditional disposal, the field specified in the nameof expression must be a boolean field. Properties and methods are not supported for the current version.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has 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
4.0.0 97 9/15/2026
3.0.0 139 5/26/2026
2.0.0 127 4/14/2026
1.2.0 130 4/12/2026
1.1.1 118 4/9/2026
1.1.0 143 4/9/2026 1.1.0 is deprecated because it has critical bugs.