Dirge by Kazuki Kohzuki
NuGet / site data
Details
Info
Name: Dirge
Disposable Implementation Roslyn Generator Extension
Author: Kazuki Kohzuki
NuGet: https://www.nuget.org/packages/Dirge/
You can find more details at https://github.com/IkuzakIkuzok/Dirge
Author
Kazuki Kohzuki

Original Readme
Dirge
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);
}
}
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.
About
Dirge is a Roslyn-based code generator that automatically implements the IDisposable pattern for C# classes.
Key Features:
-
AutoDispose Attribute: Mark any partial class with
[AutoDispose]to automatically generateDispose()andDispose(bool)methods. -
Automatic Field Disposal: The generator detects all disposable fields and automatically calls
.Dispose()on them in the correct order.
How to use
Example (source csproj, source files)
- CSharp Project
- Program.cs
- DALDB.cs
- ConnectionDB.cs
This is the CSharp Project that references Dirge
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dirge" Version="3.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
This is the use of Dirge in Program.cs
using IDisposableGeneratorDemo;
//https://github.com/benutomo-dev/RoslynComponents
using (var db = new DALDB())
{
Console.WriteLine("before releasing");
}
Console.WriteLine("after releasing");
This is the use of Dirge in DALDB.cs
namespace IDisposableGeneratorDemo;
[Dirge.AutoDispose]
partial class DALDB
{
private ConnectionDB cn;
private ConnectionDB cn1;
public DALDB()
{
cn = new ConnectionDB();
cn1=new ConnectionDB();
}
}
This is the use of Dirge in ConnectionDB.cs
namespace IDisposableGeneratorDemo;
class ConnectionDB : IDisposable
{
static int count = 0;
public ConnectionDB()
{
Interlocked.Increment(ref count);
}
public void Dispose()
{
Console.WriteLine($"disposing connectiondb {Interlocked.Decrement(ref count)}");
}
}
Generated Files
Those are taken from $(BaseIntermediateOutputPath)\GX
- DALDB.GeneratedDispose.g.cs
- AutoDisposeAttribute.g.cs
- DoNotDisposeAttribute.g.cs
- DoNotDisposeWhenAttribute.g.cs
// <auto-generated/>
#pragma warning disable CS0282
namespace IDisposableGeneratorDemo
{
partial class DALDB : global::System.IDisposable
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
[global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)]
[global::System.Runtime.CompilerServices.CompilerGenerated]
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.cn?.Dispose();
this.cn1?.Dispose();
}
}
finally
{
this.__generated_disposed = true;
}
}
}
}
// <auto-generated />
#nullable enable
namespace Dirge
{
[global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
internal sealed class AutoDisposeAttribute : global::System.Attribute
{
public string? ReleaseUnmanagedResources \{ get; set; \} = null;
internal AutoDisposeAttribute() \{ }
}
}
// <auto-generated />
namespace Dirge
{
[global::System.AttributeUsage(global::System.AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
internal sealed class DoNotDisposeAttribute : global::System.Attribute
{
internal DoNotDisposeAttribute() \{ }
}
}
// <auto-generated />
namespace Dirge
{
[global::System.AttributeUsage(global::System.AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
internal sealed class DoNotDisposeWhenAttribute : global::System.Attribute
{
internal string FlagName \{ get; }
internal bool FlagCondition \{ get; }
internal DoNotDisposeWhenAttribute(string flagName, bool flagCondition)
{
this.FlagName = flagName;
this.FlagCondition = flagCondition;
}
}
}
Useful
Download Example (.NET C#)
Share Dirge
https://ignatandrei.github.io/RSCG_Examples/v2/docs/Dirge
Category "Disposer" has the following generators:
1 BenutomoAutomaticDisposeImplSourceGenerator
2023-08-15
2 Coplt.Dropping
2024-08-13
3 Dirge
2026-07-01
4 DisposableHelpers
2023-10-09
5 Disposer
2023-10-03
6 GenerateDispose
2026-05-13
7 IDisposableGenerator
2023-10-11