From e2f827ceec97bb4544edb76b80f76c474444d765 Mon Sep 17 00:00:00 2001 From: Andrei Ignat Date: Wed, 19 Aug 2026 07:11:18 +0300 Subject: [PATCH 1/5] first --- v2/RSCGExamplesData/GeneratorDataRec.json | 6 +++++ .../description.json | 22 ++++++++++++++++ .../src/IDisp.sln | 25 +++++++++++++++++++ .../src/IDisp/ConnectionDB.cs | 15 +++++++++++ .../src/IDisp/DALDB.cs | 19 ++++++++++++++ .../src/IDisp/IDisp.csproj | 17 +++++++++++++ .../src/IDisp/Program.cs | 7 ++++++ 7 files changed, 111 insertions(+) create mode 100644 v2/rscg_examples/ReflectionIT.DisposeGenerator/description.json create mode 100644 v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp.sln create mode 100644 v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/ConnectionDB.cs create mode 100644 v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/DALDB.cs create mode 100644 v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/IDisp.csproj create mode 100644 v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/Program.cs diff --git a/v2/RSCGExamplesData/GeneratorDataRec.json b/v2/RSCGExamplesData/GeneratorDataRec.json index d6f25daea..e658bbcb6 100644 --- a/v2/RSCGExamplesData/GeneratorDataRec.json +++ b/v2/RSCGExamplesData/GeneratorDataRec.json @@ -1684,5 +1684,11 @@ "Category": 48, "dtStart": "2026-08-18T00:00:00", "show": true + }, + { + "ID": "ReflectionIT.DisposeGenerator", + "Category": 18, + "dtStart": "2026-08-19T00:00:00", + "show": true } ] \ No newline at end of file diff --git a/v2/rscg_examples/ReflectionIT.DisposeGenerator/description.json b/v2/rscg_examples/ReflectionIT.DisposeGenerator/description.json new file mode 100644 index 000000000..2c1b59530 --- /dev/null +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/description.json @@ -0,0 +1,22 @@ +{ + "generator":{ + "name":"ReflectionIT.DisposeGenerator", + "nuget":[ + "https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/" + ], + "link":"https://github.com/sonnemaf/ReflectionIT.DisposeGenerator", + "author":"Fons Sonnemans", + "source":"https://github.com/sonnemaf/ReflectionIT.DisposeGenerator" + }, + "data":{ + "goodFor":["Automatically implements the IDisposable pattern for classes that contain disposable fields."], + "csprojDemo":"IDisp.csproj", + "csFiles":["Program.cs","ConnectionDB.cs"], + "excludeDirectoryGenerated":[""], + "includeAdditionalFiles":[""] + }, + "links":{ + "blog":"", + "video":"" + } +} \ No newline at end of file diff --git a/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp.sln b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp.sln new file mode 100644 index 000000000..ad2677ba6 --- /dev/null +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33829.357 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IDisp", "IDisp\IDisp.csproj", "{6237DD9A-6211-4046-97B4-754E12478992}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6237DD9A-6211-4046-97B4-754E12478992}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6237DD9A-6211-4046-97B4-754E12478992}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6237DD9A-6211-4046-97B4-754E12478992}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6237DD9A-6211-4046-97B4-754E12478992}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {81F892FB-0998-4DEA-92E1-55B8EECB0608} + EndGlobalSection +EndGlobal diff --git a/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/ConnectionDB.cs b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/ConnectionDB.cs new file mode 100644 index 000000000..f730082e7 --- /dev/null +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/ConnectionDB.cs @@ -0,0 +1,15 @@ +using ReflectionIT.DisposeGenerator.Attributes; + +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)}"); + } +} diff --git a/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/DALDB.cs b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/DALDB.cs new file mode 100644 index 000000000..858b553a3 --- /dev/null +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/DALDB.cs @@ -0,0 +1,19 @@ +using ReflectionIT.DisposeGenerator.Attributes; + +namespace IDisposableGeneratorDemo; + +[Disposable] +partial class DALDB :IDisposable +{ + [Dispose] + private ConnectionDB cn; + [Dispose] + private ConnectionDB cn1; + + public DALDB() + { + cn = new ConnectionDB(); + cn1=new ConnectionDB(); + } + +} diff --git a/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/IDisp.csproj b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/IDisp.csproj new file mode 100644 index 000000000..56c0be781 --- /dev/null +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/IDisp.csproj @@ -0,0 +1,17 @@ + + + + Exe + net10.0 + enable + enable + + + true + $(BaseIntermediateOutputPath)\GX + + + + + + diff --git a/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/Program.cs b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/Program.cs new file mode 100644 index 000000000..601e1dcde --- /dev/null +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/Program.cs @@ -0,0 +1,7 @@ +using IDisposableGeneratorDemo; +//https://github.com/benutomo-dev/RoslynComponents +using (var db = new DALDB()) +{ + Console.WriteLine("before releasing"); +} +Console.WriteLine("after releasing"); \ No newline at end of file From 1b9124d541a31c6e90642e85e341c77545ea2a27 Mon Sep 17 00:00:00 2001 From: Andrei Ignat Date: Wed, 19 Aug 2026 07:12:26 +0300 Subject: [PATCH 2/5] second --- README.md | 30 +- later.md | 2 +- v2/Generator/all.csv | 1 + .../ReflectionIT.DisposeGenerator/nuget.txt | 1 + .../ReflectionIT.DisposeGenerator/readme.txt | 506 ++++++++++++ .../.tours/ReflectionIT.DisposeGenerator.tour | 36 + .../ReflectionIT.DisposeGenerator/video.json | 39 + .../docs/Authors/Fons_Sonnemans.md | 4 +- .../docs/Categories/Disposer.md | 4 +- .../docs/Categories/_PrimitiveDisposer.mdx | 2 + .../ReflectionIT.DisposeGenerator.md | 747 ++++++++++++++++++ .../docs/RSCG-Examples/index.md | 13 +- v2/rscg_examples_site/docs/about.md | 2 +- v2/rscg_examples_site/docs/indexRSCG.md | 5 +- .../src/components/HomepageFeatures/index.js | 2 +- .../static/exports/RSCG.json | 8 + .../static/exports/RSCG.xlsx | Bin 14566 -> 14611 bytes 17 files changed, 1388 insertions(+), 14 deletions(-) create mode 100644 v2/rscg_examples/ReflectionIT.DisposeGenerator/nuget.txt create mode 100644 v2/rscg_examples/ReflectionIT.DisposeGenerator/readme.txt create mode 100644 v2/rscg_examples/ReflectionIT.DisposeGenerator/src/.tours/ReflectionIT.DisposeGenerator.tour create mode 100644 v2/rscg_examples/ReflectionIT.DisposeGenerator/video.json create mode 100644 v2/rscg_examples_site/docs/RSCG-Examples/ReflectionIT.DisposeGenerator.md diff --git a/README.md b/README.md index c99573bef..6d3581c03 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# RSCG - 278 Examples of Roslyn Source Code Generators / 16 created by Microsoft / +# RSCG - 279 Examples of Roslyn Source Code Generators / 16 created by Microsoft / -The RSCG_Examples repository is a comprehensive documentation system that automatically processes and showcases 278 Roslyn Source Code Generator (RSCG) examples. The system transforms individual RSCG projects into structured documentation with code examples and cross-referenced content with a searchable website and code example exports. +The RSCG_Examples repository is a comprehensive documentation system that automatically processes and showcases 279 Roslyn Source Code Generator (RSCG) examples. The system transforms individual RSCG projects into structured documentation with code examples and cross-referenced content with a searchable website and code example exports. This system serves as both a learning resource for .NET developers interested in source generators and an automated pipeline for maintaining up-to-date documentation about the RSCG ecosystem -## Latest Update : 2026-08-18 => 18 August 2026 +## Latest Update : 2026-08-19 => 19 August 2026 If you want to see examples with code, please click ***[List V2](https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG)*** @@ -24,8 +24,30 @@ If you want to be notified each time I add a new RSCG example , please click htt ## Content -Those are the 278 Roslyn Source Code Generators that I have tested you can see and download source code example. +Those are the 279 Roslyn Source Code Generators that I have tested you can see and download source code example. ( including 16 from Microsoft ) +### 279. [ReflectionIT.DisposeGenerator](https://ignatandrei.github.io/RSCG_Examples/v2/docs/ReflectionIT.DisposeGenerator) , in the [Disposer](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#disposer) category + +Generated on : 2026-08-19 => 19 August 2026 + +
+ Expand + + + +Author: Fons Sonnemans + +Package Description + +Nuget: [https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/](https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/) + + +Link: [https://ignatandrei.github.io/RSCG_Examples/v2/docs/ReflectionIT.DisposeGenerator](https://ignatandrei.github.io/RSCG_Examples/v2/docs/ReflectionIT.DisposeGenerator) + +Source: [https://github.com/sonnemaf/ReflectionIT.DisposeGenerator](https://github.com/sonnemaf/ReflectionIT.DisposeGenerator) + +
+ ### 278. [Atulin.AutoDbSet](https://ignatandrei.github.io/RSCG_Examples/v2/docs/Atulin.AutoDbSet) , in the [EntityFramework](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#entityframework) category Generated on : 2026-08-18 => 18 August 2026 diff --git a/later.md b/later.md index 7c77384b0..c482172ea 100644 --- a/later.md +++ b/later.md @@ -1,6 +1,6 @@ # Just later -## Latest Update : 2026-08-18 => 18 August 2026 +## Latest Update : 2026-08-19 => 19 August 2026 diff --git a/v2/Generator/all.csv b/v2/Generator/all.csv index 348301fff..7e51dc849 100644 --- a/v2/Generator/all.csv +++ b/v2/Generator/all.csv @@ -277,3 +277,4 @@ Nr,Key,Source,Category 276,TinyBDD.MSTest, https://github.com/JerrettDavis/TinyBDD,Tests 277,Bennewitz.Ninja.AutoVersioning, https://github.com/JanusMael/Bennewitz.Ninja.AutoVersioning,EnhancementProject 278,Atulin.AutoDbSet, https://github.com/Atulin/AutoDbSet,EntityFramework +279,ReflectionIT.DisposeGenerator, https://github.com/sonnemaf/ReflectionIT.DisposeGenerator,Disposer diff --git a/v2/rscg_examples/ReflectionIT.DisposeGenerator/nuget.txt b/v2/rscg_examples/ReflectionIT.DisposeGenerator/nuget.txt new file mode 100644 index 000000000..882e74f60 --- /dev/null +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/nuget.txt @@ -0,0 +1 @@ +Package Description \ No newline at end of file diff --git a/v2/rscg_examples/ReflectionIT.DisposeGenerator/readme.txt b/v2/rscg_examples/ReflectionIT.DisposeGenerator/readme.txt new file mode 100644 index 000000000..f1e23e917 --- /dev/null +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/readme.txt @@ -0,0 +1,506 @@ +# ReflectionIT.DisposeGenerator + +A source generator package that implements the dispose and async dispose patterns. + +- https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/dispose-pattern +- https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose +- https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-disposeasync + +## NuGet package + +| Package | Version | +| ------ | ------ | +| ReflectionIT.DisposeGenerator | [![NuGet](https://img.shields.io/nuget/v/ReflectionIT.DisposeGenerator)](https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/) | + +## Installation + +```xml + +``` + +## Quick start + +Annotate a **partial** class or struct with the `Disposable` attribute and mark disposable fields or properties with `Dispose`. + +```cs +using System; +using System.IO; +using ReflectionIT.DisposeGenerator.Attributes; + +[Disposable] +public partial class LogWriter : IDisposable { + + [Dispose] + private readonly StreamWriter _streamWriter; + + public LogWriter(string path) => _streamWriter = new StreamWriter(path); + + public void WriteLine(string text) { + ThrowIfDisposed(); + _streamWriter.WriteLine($"{DateTime.Now}\t{text}"); + } +} +``` + +The generator creates the dispose members for the annotated type, including `_isDisposed`, an `IsDisposed` property, and `ThrowIfDisposed()` by default. + +## Requirements and diagnostics + +- The annotated type must be `partial`. +- `[Disposable]` can be applied to classes and structs. +- `[Dispose]` and `[AsyncDispose]` can be applied to fields and properties. +- The generator emits `RITDG001` when a type annotated with `[Disposable]` is not declared `partial`. +- Members annotated with `[Dispose]` or `[AsyncDispose]` must support the generated dispose call pattern. Otherwise the generated code can produce compiler errors. + +### RITDG001 + +`RITDG001`: Type `'{typeName}'` is annotated with `[Disposable]` and must be declared partial for ReflectionIT.DisposeGenerator to generate code. + +## Attribute reference + +### `DisposableAttribute` + +| Property | Default | Description | +| --- | --- | --- | +| `OverrideDispose` | `false` | Generates `Dispose(bool)` as an override instead of generating a public `Dispose()` method. | +| `OverrideDisposeAsyncCore` | `false` | Generates `DisposeAsyncCore()` as an override instead of generating a public `DisposeAsync()` method. | +| `GenerateThrowIfDisposed` | `true` | Generates `ThrowIfDisposed()` for guarding public instance members. | +| `ExplicitInterfaceImplementation` | `false` | Generates explicit `IDisposable.Dispose()` and `IAsyncDisposable.DisposeAsync()` implementations when applicable. | +| `IsThreadSafe` | `false` | Uses thread-safe disposal state transitions via `Interlocked.CompareExchange`. | +| `HasUnmanagedResources` | `false` | Adds a finalizer and `ReleaseUnmanagedResources()` partial method support. | + +### `DisposeAttribute` + +| Property | Default | Description | +| --- | --- | --- | +| `SetToNull` | `false` | Sets the annotated field or property to `null` after disposal. | + +### `AsyncDisposeAttribute` + +| Property | Default | Description | +| --- | --- | --- | +| `SetToNull` | `false` | Sets the annotated field or property to `null` after asynchronous disposal. | +| `ConfigureAwait` | `true` | Controls the `ConfigureAwait(...)` value used for generated async disposal calls. | + +## What gets generated + +Depending on the options and the annotated members, the generator can create: + +- `Dispose()` +- `Dispose(bool)` +- `DisposeAsync()` +- `DisposeAsyncCore()` +- `IsDisposed` +- `ThrowIfDisposed()` +- `_isDisposed` +- a finalizer +- `ReleaseUnmanagedResources()` + +## Recommended use of `ThrowIfDisposed` + +Call `ThrowIfDisposed()` at the start of public instance members that depend on resources managed by the generated dispose pattern. The generated method uses the generated `IsDisposed` property, so derived types can customize disposed-state checks through `IsDisposed` instead of overriding `ThrowIfDisposed()`. + +```cs +using ReflectionIT.DisposeGenerator.Attributes; + +[Disposable] +public partial class LogWriter : IDisposable { + + [Dispose] + private readonly StreamWriter _streamWriter; + + public LogWriter(string path) => _streamWriter = new StreamWriter(path); + + public void WriteLine(string text) { + ThrowIfDisposed(); + _streamWriter.WriteLine($"{DateTime.Now}\t{text}"); + } +} +``` + +This helps fail fast with an `ObjectDisposedException` when the instance is used after it has been disposed. + +## `SetToNull` usage + +Use `SetToNull` when the property or field should be set to `null` after disposal. + +```cs +using ReflectionIT.DisposeGenerator.Attributes; + +[Disposable] +public partial class LogWriter : IDisposable { + + [Dispose(SetToNull = true)] + private StreamWriter StreamWriter { get; set; } + + public LogWriter(string path) => StreamWriter = new StreamWriter(path); + + public void WriteLine(string text) { + ThrowIfDisposed(); + StreamWriter.WriteLine($"{DateTime.Now}\t{text}"); + } +} +``` + +This generates the following partial class, which disposes the `StreamWriter` property and sets it to `null`. + +```cs +partial class LogWriter +{ + /// + /// Releases all resources used by the current instance. + /// + public void Dispose() { + Dispose(disposing: true); + global::System.GC.SuppressFinalize(this); + } + + /// + /// Tracks whether the current instance has been disposed. This field must not be modified manually. + /// + private bool _isDisposed; + + /// + /// Gets a value indicating whether the current instance has been disposed. + /// + protected virtual bool IsDisposed => _isDisposed; + + /// + /// Throws an exception if the current instance has been disposed. + /// + protected void ThrowIfDisposed() { + if (IsDisposed) { + throw new global::System.ObjectDisposedException(nameof(LogWriter)); + } + } + + /// + /// Releases the unmanaged resources used by the current instance and optionally releases the managed resources. + /// + /// "true" to release managed resources; otherwise, "false". + protected virtual void Dispose(bool disposing) { + if (_isDisposed) { + return; + } + _isDisposed = true; + if (disposing) { + this.StreamWriter?.Dispose(); + } + StreamWriter = null; + } + +} +``` + +## Async dispose + +Use `AsyncDispose` for fields or properties that support `DisposeAsync()`. + +```cs +using System; +using System.IO; +using ReflectionIT.DisposeGenerator.Attributes; + +[Disposable] +public partial class LogWriter : IAsyncDisposable { + + [AsyncDispose] + private readonly StreamWriter _streamWriter; + + public LogWriter(string path) => _streamWriter = new StreamWriter(path); +} +``` + +This generates `DisposeAsync()` and `DisposeAsyncCore()`. If the same member also has `[Dispose]`, the generator supports both sync and async cleanup patterns. + +```cs +partial class LogWriter +{ + /// + /// Asynchronously releases all resources used by the current instance. + /// + /// + /// A task that represents the asynchronous dispose operation. + /// + public async global::System.Threading.Tasks.ValueTask DisposeAsync() { + await DisposeAsyncCore().ConfigureAwait(false); + global::System.GC.SuppressFinalize(this); + } + + /// + /// Tracks whether the current instance has been disposed. This field must not be modified manually. + /// + private bool _isDisposed; + + /// + /// Gets a value indicating whether the current instance has been disposed. + /// + protected virtual bool IsDisposed => _isDisposed; + + /// + /// Throws an exception if the current instance has been disposed. + /// + protected void ThrowIfDisposed() { + if (IsDisposed) { + throw new global::System.ObjectDisposedException(nameof(LogWriter)); + } + } + + /// + /// Asynchronously releases the resources used by the current instance. + /// + /// + /// A task that represents the asynchronous dispose operation. + /// + protected virtual async global::System.Threading.Tasks.ValueTask DisposeAsyncCore() { + if (_isDisposed) { + return; + } + _isDisposed = true; + if (this._streamWriter != null) { + await this._streamWriter.DisposeAsync().ConfigureAwait(false); + } + } + +} +``` + +## Implement the dispose pattern for a derived class + +A class derived from a class that already implements `IDisposable` should not implement `IDisposable` again, because the base implementation of `IDisposable.Dispose` is inherited by derived classes. + +Set `OverrideDispose = true` for the derived partial class. In that case, the public parameterless `Dispose()` method is not generated, and the protected `Dispose(bool)` method is generated as an override instead. + +```cs +[Disposable(OverrideDispose = true)] +public partial class SecondLogWriter : LogWriter { + + [Dispose] + private StreamWriter SecondStreamWriter { get; } + + public SecondLogWriter(string path) : base(path) => SecondStreamWriter = new StreamWriter(path + "2"); + + public override void WriteLine(string text) { + base.WriteLine(text); + SecondStreamWriter.WriteLine($"{DateTime.Now}\t{text.ToUpper()}"); + } +} +``` + +This generates the following partial class, which disposes the `SecondStreamWriter` property. + +```cs +partial class SecondLogWriter +{ + /// + /// Tracks whether the current instance has been disposed. This field must not be modified manually. + /// + private bool _isDisposed; + + /// + /// Gets a value indicating whether the current instance has been disposed. + /// + protected override bool IsDisposed => _isDisposed || base.IsDisposed; + + /// + /// Throws an exception if the current instance has been disposed. + /// + protected void ThrowIfDisposed() { + if (IsDisposed) { + throw new global::System.ObjectDisposedException(nameof(SecondLogWriter)); + } + } + + /// + /// Releases the unmanaged resources used by the current instance and optionally releases the managed resources. + /// + /// "true" to release managed resources; otherwise, "false". + protected override void Dispose(bool disposing) { + if (_isDisposed) { + return; + } + _isDisposed = true; + if (disposing) { + this.SecondStreamWriter?.Dispose(); + } + base.Dispose(disposing); + } + +} +``` + +## Unmanaged resources + +Set `HasUnmanagedResources = true` to include unmanaged resource cleanup support. +Then implement the partial method `ReleaseUnmanagedResources()`, which releases the unmanaged resource. + +If you need to work with unmanaged resources, it is strongly recommended to wrap the unmanaged `IntPtr` handle in a [SafeHandle](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose#safe-handles). + +```cs +[Disposable(HasUnmanagedResources = true)] +public partial class LogWriterWithAnExtraIntPtr : IDisposable { + + private readonly IntPtr _pointer; + + [Dispose] + private StreamWriter StreamWriter { get; } + + public LogWriterWithAnExtraIntPtr(string path) { + StreamWriter = new StreamWriter(path); + _pointer = Marshal.AllocHGlobal(cb: 128); + } + + public void WriteLine(string text) => StreamWriter.WriteLine($"{DateTime.Now}\t{text}"); + + protected virtual partial void ReleaseUnmanagedResources() => Marshal.FreeHGlobal(_pointer); +} +``` + +This generates the following partial class with a finalizer and a partial method named `ReleaseUnmanagedResources()` that you must implement. + +```cs +partial class LogWriterWithAnExtraIntPtr +{ + /// + /// Releases all resources used by the current instance. + /// + public void Dispose() { + Dispose(disposing: true); + global::System.GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged resources held by the current instance. + /// + ~LogWriterWithAnExtraIntPtr() { + Dispose(disposing: false); + } + + /// + /// Releases unmanaged resources held by the current instance. + /// + protected virtual partial void ReleaseUnmanagedResources(); + + /// + /// Tracks whether the current instance has been disposed. This field must not be modified manually. + /// + private bool _isDisposed; + + /// + /// Gets a value indicating whether the current instance has been disposed. + /// + protected virtual bool IsDisposed => _isDisposed; + + /// + /// Throws an exception if the current instance has been disposed. + /// + protected void ThrowIfDisposed() { + if (IsDisposed) { + throw new global::System.ObjectDisposedException(nameof(LogWriterWithAnExtraIntPtr)); + } + } + + /// + /// Releases the unmanaged resources used by the current instance and optionally releases the managed resources. + /// + /// "true" to release managed resources; otherwise, "false". + protected virtual void Dispose(bool disposing) { + if (_isDisposed) { + return; + } + _isDisposed = true; + if (disposing) { + this.StreamWriter?.Dispose(); + } + ReleaseUnmanagedResources(); + } + +} +``` + +## Thread-safe disposal + +Use `IsThreadSafe = true` to ensure thread-safe disposal via `Interlocked.CompareExchange`. + +```cs +[Disposable(IsThreadSafe = true)] +public partial class LogWriter : IDisposable { + + [Dispose] + private readonly StreamWriter _streamWriter; + + public LogWriter(string path) => _streamWriter = new StreamWriter(path); + + public void WriteLine(string text) => _streamWriter.WriteLine($"{DateTime.Now}\t{text}"); +} +``` + +This generates the following partial class, which uses `Interlocked.CompareExchange` to ensure thread-safe disposal. + +```cs +partial class LogWriter +{ + /// + /// Releases all resources used by the current instance. + /// + public void Dispose() { + Dispose(disposing: true); + global::System.GC.SuppressFinalize(this); + } + + /// + /// Detects redundant Dispose() calls in a thread-safe manner. _isDisposed == 0 means Dispose(bool) has not been called yet, and _isDisposed == 1 means Dispose(bool) has already been called. This field must not be modified manually. + /// + private int _isDisposed; + + /// + /// Gets a value indicating whether the current instance has been disposed. + /// + protected virtual bool IsDisposed => _isDisposed != 0; + + /// + /// Throws an exception if the current instance has been disposed. + /// + protected void ThrowIfDisposed() { + if (IsDisposed) { + throw new global::System.ObjectDisposedException(nameof(LogWriter)); + } + } + + /// + /// Releases the unmanaged resources used by the current instance and optionally releases the managed resources. + /// + /// "true" to release managed resources; otherwise, "false". + protected virtual void Dispose(bool disposing) { + if (global::System.Threading.Interlocked.CompareExchange(ref _isDisposed, 1, 0) != 0) { + return; + } + if (disposing) { + this._streamWriter?.Dispose(); + } + } + +} +``` + +## Troubleshooting + +### Why do I get `RITDG001`? + +The type marked with `[Disposable]` is not declared `partial`. Add the `partial` keyword to the class or struct declaration. + +### Why do I get compiler errors for `Dispose()` or `DisposeAsync()` on an annotated member? + +The generator emits calls to `Dispose()` for `[Dispose]` members and `DisposeAsync()` for `[AsyncDispose]` members. Make sure the annotated member supports the corresponding API. + +### Why was no code generated? + +Common reasons: + +- the type is not `partial` +- no fields or properties were annotated with `[Dispose]` or `[AsyncDispose]` +- the type only has invalid attribute usage that prevents successful compilation + +## License + +This project is licensed under the MIT License. See the [LICENSE](LICENSE.txt) file for details. diff --git a/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/.tours/ReflectionIT.DisposeGenerator.tour b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/.tours/ReflectionIT.DisposeGenerator.tour new file mode 100644 index 000000000..3601ad15d --- /dev/null +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/.tours/ReflectionIT.DisposeGenerator.tour @@ -0,0 +1,36 @@ + +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "ReflectionIT.DisposeGenerator", + "steps": + [ + { + "file": "IDisp/IDisp.csproj", + "description": "First, we add Nuget [ReflectionIT.DisposeGenerator](https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/) in csproj ", + "pattern": "ReflectionIT.DisposeGenerator" + } + + ,{ + "file": "IDisp/ConnectionDB.cs", + "description": "File ConnectionDB.cs ", + "pattern": "this is the code" + } + + ,{ + "file": "IDisp/Program.cs", + "description": "File Program.cs \r\n>> dotnet run --project IDisp/IDisp.csproj ", + "pattern": "this is the code" + } + + + ,{ + "file": "IDisp/obj/GX/ReflectionIT.DisposeGenerator/ReflectionIT.DisposeGenerator.SourceGenerator/IDisposableGeneratorDemo.DALDB.g.cs", + "description": "Generated File 1 from 1 : IDisposableGeneratorDemo.DALDB.g.cs ", + "line": 1 + } + + ], + + "ref": "main" + +} \ No newline at end of file diff --git a/v2/rscg_examples/ReflectionIT.DisposeGenerator/video.json b/v2/rscg_examples/ReflectionIT.DisposeGenerator/video.json new file mode 100644 index 000000000..d23e28a6c --- /dev/null +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/video.json @@ -0,0 +1,39 @@ +{ + "scriptName": "ReflectionIT.DisposeGenerator", + "steps": +[ + {"typeStep":"exec","arg":"clipchamp.exe launch"}, + {"typeStep":"text","arg": "Welcome to Roslyn Examples"}, + {"typeStep":"text","arg":"If you want to see more examples , see List Of RSCG"}, + {"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG"}, + {"typeStep":"text","arg": "My name is Andrei Ignat and I am deeply fond of Roslyn Source Code Generator. "}, + +{"typeStep":"text","arg": "Today I will present ReflectionIT.DisposeGenerator . Automatically implements the IDisposable pattern for classes that contain disposable fields. ."}, +{"typeStep":"browser","arg":"https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/"}, +{"typeStep":"text","arg": "The whole example is here"}, +{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/ReflectionIT.DisposeGenerator"}, +{"typeStep":"text","arg": "You can download the code from here"}, +{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/ReflectionIT.DisposeGenerator#download-example-net--c-"}, +{"typeStep":"text","arg":"Here is the code downloaded "}, +{"typeStep":"exec","arg":"explorer.exe /select,D:\\gth\\RSCG_Examples\\v2\\rscg_examples\\ReflectionIT.DisposeGenerator\\src\\IDisp.sln"}, +{"typeStep":"text","arg": "So , let's start the project with Visual Studio Code "}, +{"typeStep":"stepvscode","arg": "-n D:\\gth\\RSCG_Examples\\v2\\rscg_examples\\ReflectionIT.DisposeGenerator\\src"}, + +{"typeStep":"text","arg": "To use it ,you will put the Nuget ReflectionIT.DisposeGenerator into the csproj "}, + +{"typeStep":"stepvscode","arg": "-r -g D:\\gth\\RSCG_Examples\\v2\\rscg_examples\\ReflectionIT.DisposeGenerator\\src\\IDisp\\IDisp.csproj"}, + +{"typeStep":"text","arg": "And now I will show you an example of using ReflectionIT.DisposeGenerator"}, + +{"typeStep":"hide","arg": "now execute the tour in VSCode"}, +{"typeStep":"tour", "arg": "src/.tours/"}, +{"typeStep":"text","arg":" And I will execute the project"}, +{"typeStep":"showproj", "arg":"IDisp.csproj"}, +{"typeStep":"text","arg":" This concludes the project"}, +{"typeStep":"waitseconds","arg":"30"}, +{"typeStep":"text","arg": "Remember, you can download the code from here"}, +{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/ReflectionIT.DisposeGenerator#download-example-net--c-", +SpeakTest=" "}, +{"typeStep":"waitseconds","arg":"30"}, +] +} diff --git a/v2/rscg_examples_site/docs/Authors/Fons_Sonnemans.md b/v2/rscg_examples_site/docs/Authors/Fons_Sonnemans.md index f0e1ad458..690cbf5c7 100644 --- a/v2/rscg_examples_site/docs/Authors/Fons_Sonnemans.md +++ b/v2/rscg_examples_site/docs/Authors/Fons_Sonnemans.md @@ -1,7 +1,9 @@ # Author : Fons Sonnemans -Number RSCG: 1 +Number RSCG: 2 1 [Comparison](/docs/Comparison) [![Nuget](https://img.shields.io/nuget/dt/ReflectionIT.ComparisonOperatorsGenerator?label=ReflectionIT.ComparisonOperatorsGenerator)](https://www.nuget.org/packages/ReflectionIT.ComparisonOperatorsGenerator/) ![GitHub Repo stars](https://img.shields.io/github/stars/sonnemaf/ReflectionIT.ComparisonOperatorsGenerator?style=social) 2025-05-25 + 2 [ReflectionIT.DisposeGenerator](/docs/ReflectionIT.DisposeGenerator) [![Nuget](https://img.shields.io/nuget/dt/ReflectionIT.DisposeGenerator?label=ReflectionIT.DisposeGenerator)](https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/) ![GitHub Repo stars](https://img.shields.io/github/stars/sonnemaf/ReflectionIT.DisposeGenerator?style=social) 2026-08-19 + diff --git a/v2/rscg_examples_site/docs/Categories/Disposer.md b/v2/rscg_examples_site/docs/Categories/Disposer.md index d5225746d..c2c207532 100644 --- a/v2/rscg_examples_site/docs/Categories/Disposer.md +++ b/v2/rscg_examples_site/docs/Categories/Disposer.md @@ -1,6 +1,6 @@

Disposer

-Number RSCG: 7 +Number RSCG: 8 1 [BenutomoAutomaticDisposeImplSourceGenerator](/docs/BenutomoAutomaticDisposeImplSourceGenerator) [![Nuget](https://img.shields.io/nuget/dt/Benutomo.AutomaticDisposeImpl.SourceGenerator?label=Benutomo.AutomaticDisposeImpl.SourceGenerator)](https://www.nuget.org/packages/Benutomo.AutomaticDisposeImpl.SourceGenerator/) ![GitHub Repo stars](https://img.shields.io/github/stars/benutomo-dev/RoslynComponents?style=social) 2023-08-15 @@ -15,4 +15,6 @@ Number RSCG: 7 6 [GenerateDispose](/docs/GenerateDispose) [![Nuget](https://img.shields.io/nuget/dt/GenerateDispose?label=GenerateDispose)](https://www.nuget.org/packages/GenerateDispose/) ![GitHub Repo stars](https://img.shields.io/github/stars/ItaiTzur76/GenerateDispose?style=social) 2026-05-13 7 [IDisposableGenerator](/docs/IDisposableGenerator) [![Nuget](https://img.shields.io/nuget/dt/IDisposableGenerator?label=IDisposableGenerator)](https://www.nuget.org/packages/IDisposableGenerator/) ![GitHub Repo stars](https://img.shields.io/github/stars/Elskom/IDisposableGenerator?style=social) 2023-10-11 + + 8 [ReflectionIT.DisposeGenerator](/docs/ReflectionIT.DisposeGenerator) [![Nuget](https://img.shields.io/nuget/dt/ReflectionIT.DisposeGenerator?label=ReflectionIT.DisposeGenerator)](https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/) ![GitHub Repo stars](https://img.shields.io/github/stars/sonnemaf/ReflectionIT.DisposeGenerator?style=social) 2026-08-19 \ No newline at end of file diff --git a/v2/rscg_examples_site/docs/Categories/_PrimitiveDisposer.mdx b/v2/rscg_examples_site/docs/Categories/_PrimitiveDisposer.mdx index 28fda18bb..72fbf4c1e 100644 --- a/v2/rscg_examples_site/docs/Categories/_PrimitiveDisposer.mdx +++ b/v2/rscg_examples_site/docs/Categories/_PrimitiveDisposer.mdx @@ -14,6 +14,8 @@ 7 [IDisposableGenerator](/docs/IDisposableGenerator) [![Nuget](https://img.shields.io/nuget/dt/IDisposableGenerator?label=IDisposableGenerator)](https://www.nuget.org/packages/IDisposableGenerator/) ![GitHub Repo stars](https://img.shields.io/github/stars/Elskom/IDisposableGenerator?style=social) 2023-10-11 + 8 [ReflectionIT.DisposeGenerator](/docs/ReflectionIT.DisposeGenerator) [![Nuget](https://img.shields.io/nuget/dt/ReflectionIT.DisposeGenerator?label=ReflectionIT.DisposeGenerator)](https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/) ![GitHub Repo stars](https://img.shields.io/github/stars/sonnemaf/ReflectionIT.DisposeGenerator?style=social) 2026-08-19 + ### See category [Disposer](/docs/Categories/Disposer) diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/ReflectionIT.DisposeGenerator.md b/v2/rscg_examples_site/docs/RSCG-Examples/ReflectionIT.DisposeGenerator.md new file mode 100644 index 000000000..f7e41b573 --- /dev/null +++ b/v2/rscg_examples_site/docs/RSCG-Examples/ReflectionIT.DisposeGenerator.md @@ -0,0 +1,747 @@ +--- +sidebar_position: 2790 +title: 279 - ReflectionIT.DisposeGenerator +description: Automatically implements the IDisposable pattern for classes that contain disposable fields. +slug: /ReflectionIT.DisposeGenerator +--- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import TOCInline from '@theme/TOCInline'; +import SameCategory from '../Categories/_PrimitiveDisposer.mdx'; + +# ReflectionIT.DisposeGenerator by Fons Sonnemans + + + + +## NuGet / site data +[![Nuget](https://img.shields.io/nuget/dt/ReflectionIT.DisposeGenerator?label=ReflectionIT.DisposeGenerator)](https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/) +[![GitHub last commit](https://img.shields.io/github/last-commit/sonnemaf/ReflectionIT.DisposeGenerator?label=updated)](https://github.com/sonnemaf/ReflectionIT.DisposeGenerator) +![GitHub Repo stars](https://img.shields.io/github/stars/sonnemaf/ReflectionIT.DisposeGenerator?style=social) + +## Details + +### Info +:::info + +Name: **ReflectionIT.DisposeGenerator** + +Package Description + +Author: Fons Sonnemans + +NuGet: +*https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/* + + +You can find more details at https://github.com/sonnemaf/ReflectionIT.DisposeGenerator + +Source: https://github.com/sonnemaf/ReflectionIT.DisposeGenerator + +::: + +### Author +:::note +Fons Sonnemans +![Alt text](https://github.com/sonnemaf.png) +::: + +## Original Readme +:::note + +# ReflectionIT.DisposeGenerator + +A source generator package that implements the dispose and async dispose patterns. + +- https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/dispose-pattern +- https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose +- https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-disposeasync + +## NuGet package + +| Package | Version | +| ------ | ------ | +| ReflectionIT.DisposeGenerator | [![NuGet](https://img.shields.io/nuget/v/ReflectionIT.DisposeGenerator)](https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/) | + +## Installation + +```xml + +``` + +## Quick start + +Annotate a **partial** class or struct with the `Disposable` attribute and mark disposable fields or properties with `Dispose`. + +```cs +using System; +using System.IO; +using ReflectionIT.DisposeGenerator.Attributes; + +[Disposable] +public partial class LogWriter : IDisposable { + + [Dispose] + private readonly StreamWriter _streamWriter; + + public LogWriter(string path) => _streamWriter = new StreamWriter(path); + + public void WriteLine(string text) { + ThrowIfDisposed(); + _streamWriter.WriteLine($"{DateTime.Now}\t{text}"); + } +} +``` + +The generator creates the dispose members for the annotated type, including `_isDisposed`, an `IsDisposed` property, and `ThrowIfDisposed()` by default. + +## Requirements and diagnostics + +- The annotated type must be `partial`. +- `[Disposable]` can be applied to classes and structs. +- `[Dispose]` and `[AsyncDispose]` can be applied to fields and properties. +- The generator emits `RITDG001` when a type annotated with `[Disposable]` is not declared `partial`. +- Members annotated with `[Dispose]` or `[AsyncDispose]` must support the generated dispose call pattern. Otherwise the generated code can produce compiler errors. + +### RITDG001 + +`RITDG001`: Type `'{typeName}'` is annotated with `[Disposable]` and must be declared partial for ReflectionIT.DisposeGenerator to generate code. + +## Attribute reference + +### `DisposableAttribute` + +| Property | Default | Description | +| --- | --- | --- | +| `OverrideDispose` | `false` | Generates `Dispose(bool)` as an override instead of generating a public `Dispose()` method. | +| `OverrideDisposeAsyncCore` | `false` | Generates `DisposeAsyncCore()` as an override instead of generating a public `DisposeAsync()` method. | +| `GenerateThrowIfDisposed` | `true` | Generates `ThrowIfDisposed()` for guarding public instance members. | +| `ExplicitInterfaceImplementation` | `false` | Generates explicit `IDisposable.Dispose()` and `IAsyncDisposable.DisposeAsync()` implementations when applicable. | +| `IsThreadSafe` | `false` | Uses thread-safe disposal state transitions via `Interlocked.CompareExchange`. | +| `HasUnmanagedResources` | `false` | Adds a finalizer and `ReleaseUnmanagedResources()` partial method support. | + +### `DisposeAttribute` + +| Property | Default | Description | +| --- | --- | --- | +| `SetToNull` | `false` | Sets the annotated field or property to `null` after disposal. | + +### `AsyncDisposeAttribute` + +| Property | Default | Description | +| --- | --- | --- | +| `SetToNull` | `false` | Sets the annotated field or property to `null` after asynchronous disposal. | +| `ConfigureAwait` | `true` | Controls the `ConfigureAwait(...)` value used for generated async disposal calls. | + +## What gets generated + +Depending on the options and the annotated members, the generator can create: + +- `Dispose()` +- `Dispose(bool)` +- `DisposeAsync()` +- `DisposeAsyncCore()` +- `IsDisposed` +- `ThrowIfDisposed()` +- `_isDisposed` +- a finalizer +- `ReleaseUnmanagedResources()` + +## Recommended use of `ThrowIfDisposed` + +Call `ThrowIfDisposed()` at the start of public instance members that depend on resources managed by the generated dispose pattern. The generated method uses the generated `IsDisposed` property, so derived types can customize disposed-state checks through `IsDisposed` instead of overriding `ThrowIfDisposed()`. + +```cs +using ReflectionIT.DisposeGenerator.Attributes; + +[Disposable] +public partial class LogWriter : IDisposable { + + [Dispose] + private readonly StreamWriter _streamWriter; + + public LogWriter(string path) => _streamWriter = new StreamWriter(path); + + public void WriteLine(string text) { + ThrowIfDisposed(); + _streamWriter.WriteLine($"{DateTime.Now}\t{text}"); + } +} +``` + +This helps fail fast with an `ObjectDisposedException` when the instance is used after it has been disposed. + +## `SetToNull` usage + +Use `SetToNull` when the property or field should be set to `null` after disposal. + +```cs +using ReflectionIT.DisposeGenerator.Attributes; + +[Disposable] +public partial class LogWriter : IDisposable { + + [Dispose(SetToNull = true)] + private StreamWriter StreamWriter \{ get; set; } + + public LogWriter(string path) => StreamWriter = new StreamWriter(path); + + public void WriteLine(string text) { + ThrowIfDisposed(); + StreamWriter.WriteLine($"{DateTime.Now}\t{text}"); + } +} +``` + +This generates the following partial class, which disposes the `StreamWriter` property and sets it to `null`. + +```cs +partial class LogWriter +{ + /// + /// Releases all resources used by the current instance. + /// + public void Dispose() { + Dispose(disposing: true); + global::System.GC.SuppressFinalize(this); + } + + /// + /// Tracks whether the current instance has been disposed. This field must not be modified manually. + /// + private bool _isDisposed; + + /// + /// Gets a value indicating whether the current instance has been disposed. + /// + protected virtual bool IsDisposed => _isDisposed; + + /// + /// Throws an exception if the current instance has been disposed. + /// + protected void ThrowIfDisposed() { + if (IsDisposed) { + throw new global::System.ObjectDisposedException(nameof(LogWriter)); + } + } + + /// + /// Releases the unmanaged resources used by the current instance and optionally releases the managed resources. + /// + /// "true" to release managed resources; otherwise, "false". + protected virtual void Dispose(bool disposing) { + if (_isDisposed) { + return; + } + _isDisposed = true; + if (disposing) { + this.StreamWriter?.Dispose(); + } + StreamWriter = null; + } + +} +``` + +## Async dispose + +Use `AsyncDispose` for fields or properties that support `DisposeAsync()`. + +```cs +using System; +using System.IO; +using ReflectionIT.DisposeGenerator.Attributes; + +[Disposable] +public partial class LogWriter : IAsyncDisposable { + + [AsyncDispose] + private readonly StreamWriter _streamWriter; + + public LogWriter(string path) => _streamWriter = new StreamWriter(path); +} +``` + +This generates `DisposeAsync()` and `DisposeAsyncCore()`. If the same member also has `[Dispose]`, the generator supports both sync and async cleanup patterns. + +```cs +partial class LogWriter +{ + /// + /// Asynchronously releases all resources used by the current instance. + /// + /// + /// A task that represents the asynchronous dispose operation. + /// + public async global::System.Threading.Tasks.ValueTask DisposeAsync() { + await DisposeAsyncCore().ConfigureAwait(false); + global::System.GC.SuppressFinalize(this); + } + + /// + /// Tracks whether the current instance has been disposed. This field must not be modified manually. + /// + private bool _isDisposed; + + /// + /// Gets a value indicating whether the current instance has been disposed. + /// + protected virtual bool IsDisposed => _isDisposed; + + /// + /// Throws an exception if the current instance has been disposed. + /// + protected void ThrowIfDisposed() { + if (IsDisposed) { + throw new global::System.ObjectDisposedException(nameof(LogWriter)); + } + } + + /// + /// Asynchronously releases the resources used by the current instance. + /// + /// + /// A task that represents the asynchronous dispose operation. + /// + protected virtual async global::System.Threading.Tasks.ValueTask DisposeAsyncCore() { + if (_isDisposed) { + return; + } + _isDisposed = true; + if (this._streamWriter != null) { + await this._streamWriter.DisposeAsync().ConfigureAwait(false); + } + } + +} +``` + +## Implement the dispose pattern for a derived class + +A class derived from a class that already implements `IDisposable` should not implement `IDisposable` again, because the base implementation of `IDisposable.Dispose` is inherited by derived classes. + +Set `OverrideDispose = true` for the derived partial class. In that case, the public parameterless `Dispose()` method is not generated, and the protected `Dispose(bool)` method is generated as an override instead. + +```cs +[Disposable(OverrideDispose = true)] +public partial class SecondLogWriter : LogWriter { + + [Dispose] + private StreamWriter SecondStreamWriter \{ get; } + + public SecondLogWriter(string path) : base(path) => SecondStreamWriter = new StreamWriter(path + "2"); + + public override void WriteLine(string text) { + base.WriteLine(text); + SecondStreamWriter.WriteLine($"{DateTime.Now}\t{text.ToUpper()}"); + } +} +``` + +This generates the following partial class, which disposes the `SecondStreamWriter` property. + +```cs +partial class SecondLogWriter +{ + /// + /// Tracks whether the current instance has been disposed. This field must not be modified manually. + /// + private bool _isDisposed; + + /// + /// Gets a value indicating whether the current instance has been disposed. + /// + protected override bool IsDisposed => _isDisposed || base.IsDisposed; + + /// + /// Throws an exception if the current instance has been disposed. + /// + protected void ThrowIfDisposed() { + if (IsDisposed) { + throw new global::System.ObjectDisposedException(nameof(SecondLogWriter)); + } + } + + /// + /// Releases the unmanaged resources used by the current instance and optionally releases the managed resources. + /// + /// "true" to release managed resources; otherwise, "false". + protected override void Dispose(bool disposing) { + if (_isDisposed) { + return; + } + _isDisposed = true; + if (disposing) { + this.SecondStreamWriter?.Dispose(); + } + base.Dispose(disposing); + } + +} +``` + +## Unmanaged resources + +Set `HasUnmanagedResources = true` to include unmanaged resource cleanup support. +Then implement the partial method `ReleaseUnmanagedResources()`, which releases the unmanaged resource. + +If you need to work with unmanaged resources, it is strongly recommended to wrap the unmanaged `IntPtr` handle in a [SafeHandle](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose#safe-handles). + +```cs +[Disposable(HasUnmanagedResources = true)] +public partial class LogWriterWithAnExtraIntPtr : IDisposable { + + private readonly IntPtr _pointer; + + [Dispose] + private StreamWriter StreamWriter \{ get; } + + public LogWriterWithAnExtraIntPtr(string path) { + StreamWriter = new StreamWriter(path); + _pointer = Marshal.AllocHGlobal(cb: 128); + } + + public void WriteLine(string text) => StreamWriter.WriteLine($"{DateTime.Now}\t{text}"); + + protected virtual partial void ReleaseUnmanagedResources() => Marshal.FreeHGlobal(_pointer); +} +``` + +This generates the following partial class with a finalizer and a partial method named `ReleaseUnmanagedResources()` that you must implement. + +```cs +partial class LogWriterWithAnExtraIntPtr +{ + /// + /// Releases all resources used by the current instance. + /// + public void Dispose() { + Dispose(disposing: true); + global::System.GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged resources held by the current instance. + /// + ~LogWriterWithAnExtraIntPtr() { + Dispose(disposing: false); + } + + /// + /// Releases unmanaged resources held by the current instance. + /// + protected virtual partial void ReleaseUnmanagedResources(); + + /// + /// Tracks whether the current instance has been disposed. This field must not be modified manually. + /// + private bool _isDisposed; + + /// + /// Gets a value indicating whether the current instance has been disposed. + /// + protected virtual bool IsDisposed => _isDisposed; + + /// + /// Throws an exception if the current instance has been disposed. + /// + protected void ThrowIfDisposed() { + if (IsDisposed) { + throw new global::System.ObjectDisposedException(nameof(LogWriterWithAnExtraIntPtr)); + } + } + + /// + /// Releases the unmanaged resources used by the current instance and optionally releases the managed resources. + /// + /// "true" to release managed resources; otherwise, "false". + protected virtual void Dispose(bool disposing) { + if (_isDisposed) { + return; + } + _isDisposed = true; + if (disposing) { + this.StreamWriter?.Dispose(); + } + ReleaseUnmanagedResources(); + } + +} +``` + +## Thread-safe disposal + +Use `IsThreadSafe = true` to ensure thread-safe disposal via `Interlocked.CompareExchange`. + +```cs +[Disposable(IsThreadSafe = true)] +public partial class LogWriter : IDisposable { + + [Dispose] + private readonly StreamWriter _streamWriter; + + public LogWriter(string path) => _streamWriter = new StreamWriter(path); + + public void WriteLine(string text) => _streamWriter.WriteLine($"{DateTime.Now}\t{text}"); +} +``` + +This generates the following partial class, which uses `Interlocked.CompareExchange` to ensure thread-safe disposal. + +```cs +partial class LogWriter +{ + /// + /// Releases all resources used by the current instance. + /// + public void Dispose() { + Dispose(disposing: true); + global::System.GC.SuppressFinalize(this); + } + + /// + /// Detects redundant Dispose() calls in a thread-safe manner. _isDisposed == 0 means Dispose(bool) has not been called yet, and _isDisposed == 1 means Dispose(bool) has already been called. This field must not be modified manually. + /// + private int _isDisposed; + + /// + /// Gets a value indicating whether the current instance has been disposed. + /// + protected virtual bool IsDisposed => _isDisposed != 0; + + /// + /// Throws an exception if the current instance has been disposed. + /// + protected void ThrowIfDisposed() { + if (IsDisposed) { + throw new global::System.ObjectDisposedException(nameof(LogWriter)); + } + } + + /// + /// Releases the unmanaged resources used by the current instance and optionally releases the managed resources. + /// + /// "true" to release managed resources; otherwise, "false". + protected virtual void Dispose(bool disposing) { + if (global::System.Threading.Interlocked.CompareExchange(ref _isDisposed, 1, 0) != 0) { + return; + } + if (disposing) { + this._streamWriter?.Dispose(); + } + } + +} +``` + +## Troubleshooting + +### Why do I get `RITDG001`? + +The type marked with `[Disposable]` is not declared `partial`. Add the `partial` keyword to the class or struct declaration. + +### Why do I get compiler errors for `Dispose()` or `DisposeAsync()` on an annotated member? + +The generator emits calls to `Dispose()` for `[Dispose]` members and `DisposeAsync()` for `[AsyncDispose]` members. Make sure the annotated member supports the corresponding API. + +### Why was no code generated? + +Common reasons: + +- the type is not `partial` +- no fields or properties were annotated with `[Dispose]` or `[AsyncDispose]` +- the type only has invalid attribute usage that prevents successful compilation + +## License + +This project is licensed under the MIT License. See the [LICENSE](LICENSE.txt) file for details. + + +::: + +### About +:::note + +Automatically implements the IDisposable pattern for classes that contain disposable fields. + + +::: + +## How to use + +### Example (source csproj, source files) + + + + + +This is the CSharp Project that references **ReflectionIT.DisposeGenerator** +```xml showLineNumbers {14} + + + + Exe + net10.0 + enable + enable + + + true + $(BaseIntermediateOutputPath)\GX + + + + + + + +``` + + + + + + This is the use of **ReflectionIT.DisposeGenerator** in *Program.cs* + +```csharp showLineNumbers +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 **ReflectionIT.DisposeGenerator** in *ConnectionDB.cs* + +```csharp showLineNumbers +using ReflectionIT.DisposeGenerator.Attributes; + +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 + + + + +```csharp showLineNumbers +//------------------------------------------------------------------------------ +// +// This code was generated by the ReflectionIT.DisposeGenerator source generator +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#pragma warning disable +#nullable enable annotations + +namespace IDisposableGeneratorDemo +{ + partial class DALDB + { + /// + /// Releases all resources used by the current instance. + /// + [global::System.CodeDom.Compiler.GeneratedCode("ReflectionIT.DisposeGenerator", "0.4.2.0")] + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + public void Dispose() { + Dispose(disposing: true); + global::System.GC.SuppressFinalize(this); + } + + /// + /// Tracks whether the current instance has been disposed. This field must not be modified manually. + /// + [global::System.CodeDom.Compiler.GeneratedCode("ReflectionIT.DisposeGenerator", "0.4.2.0")] + private bool _isDisposed; + + /// + /// Gets a value indicating whether the current instance has been disposed. + /// + [global::System.CodeDom.Compiler.GeneratedCode("ReflectionIT.DisposeGenerator", "0.4.2.0")] + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + protected virtual bool IsDisposed => _isDisposed; + + /// + /// Throws an exception if the current instance has been disposed. + /// + [global::System.CodeDom.Compiler.GeneratedCode("ReflectionIT.DisposeGenerator", "0.4.2.0")] + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + protected void ThrowIfDisposed() { + if (IsDisposed) { + throw new global::System.ObjectDisposedException(nameof(DALDB)); + } + } + + /// + /// Releases the unmanaged resources used by the current instance and optionally releases the managed resources. + /// + /// "true" to release managed resources; otherwise, "false". + [global::System.CodeDom.Compiler.GeneratedCode("ReflectionIT.DisposeGenerator", "0.4.2.0")] + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + protected virtual void Dispose(bool disposing) { + if (_isDisposed) { + return; + } + _isDisposed = true; + if (disposing) { + this.cn?.Dispose(); + this.cn1?.Dispose(); + } + } + + } +} + +``` + + + + +## Useful + +### Download Example (.NET C#) + +:::tip + +[Download Example project ReflectionIT.DisposeGenerator ](/sources/ReflectionIT.DisposeGenerator.zip) + +::: + + +### Share ReflectionIT.DisposeGenerator + + + +https://ignatandrei.github.io/RSCG_Examples/v2/docs/ReflectionIT.DisposeGenerator + + + diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/index.md b/v2/rscg_examples_site/docs/RSCG-Examples/index.md index 8c8bb8058..b4b863083 100644 --- a/v2/rscg_examples_site/docs/RSCG-Examples/index.md +++ b/v2/rscg_examples_site/docs/RSCG-Examples/index.md @@ -1,7 +1,7 @@ --- sidebar_position: 30 -title: 278 RSCG list by category -description: 278 RSCG list by category +title: 279 RSCG list by category +description: 279 RSCG list by category slug: /rscg-examples --- @@ -509,7 +509,7 @@ import DocCardList from '@theme/DocCardList'; ## Disposer
- Expand Disposer =>examples:7 + Expand Disposer =>examples:8 @@ -545,6 +545,11 @@ import DocCardList from '@theme/DocCardList'; [Dirge](/docs/Dirge) + + + +[ReflectionIT.DisposeGenerator](/docs/ReflectionIT.DisposeGenerator) +
@@ -1903,6 +1908,8 @@ flowchart LR; Disposer--> Dirge((Dirge)) + Disposer--> ReflectionIT.DisposeGenerator((ReflectionIT.DisposeGenerator)) + Documentation--> REslava.ResultFlow((REslava.ResultFlow)) EnhancementClass--> System.Text.RegularExpressions((System.Text.RegularExpressions)) diff --git a/v2/rscg_examples_site/docs/about.md b/v2/rscg_examples_site/docs/about.md index 105ed1587..1e2688e03 100644 --- a/v2/rscg_examples_site/docs/about.md +++ b/v2/rscg_examples_site/docs/about.md @@ -6,7 +6,7 @@ title: About ## Content You will find here code examples -of 278 Roslyn Source Code Generator (RSCG) +of 279 Roslyn Source Code Generator (RSCG) that can be useful for you. That means, you will write more elegant and concise code - even if the generators code is not always nice to look. ## Are those examples ready for production? diff --git a/v2/rscg_examples_site/docs/indexRSCG.md b/v2/rscg_examples_site/docs/indexRSCG.md index 973078e5b..bebbc18da 100644 --- a/v2/rscg_examples_site/docs/indexRSCG.md +++ b/v2/rscg_examples_site/docs/indexRSCG.md @@ -7,9 +7,9 @@ slug: /List-of-RSCG import useBaseUrl from '@docusaurus/useBaseUrl'; -## 278 RSCG with examples in descending chronological order +## 279 RSCG with examples in descending chronological order -This is the list of 278 ( 16 from Microsoft) RSCG with examples +This is the list of 279 ( 16 from Microsoft) RSCG with examples [See by category](/docs/rscg-examples) [See as json](/exports/RSCG.json) [See as Excel](/exports/RSCG.xlsx) @@ -20,6 +20,7 @@ This is the list of 278 ( 16 from Microsoft) RSCG with examples | No | Name | Date | Category | | --------- | ----- | ---- | -------- | +|279| [ReflectionIT.DisposeGenerator by Fons Sonnemans ](/docs/ReflectionIT.DisposeGenerator)|2026-08-19 => 19 August 2026 | [Disposer](/docs/Categories/Disposer) | |278| [Atulin.AutoDbSet by Angius Atulin ](/docs/Atulin.AutoDbSet)|2026-08-18 => 18 August 2026 | [EntityFramework](/docs/Categories/EntityFramework) | |277| [Bennewitz.Ninja.AutoVersioning by Brian Bennewitz ](/docs/Bennewitz.Ninja.AutoVersioning)|2026-07-04 => 04 July 2026 | [EnhancementProject](/docs/Categories/EnhancementProject) | |276| [TinyBDD.MSTest by Jerrett Davis ](/docs/TinyBDD.MSTest)|2026-07-03 => 03 July 2026 | [Tests](/docs/Categories/Tests) | diff --git a/v2/rscg_examples_site/src/components/HomepageFeatures/index.js b/v2/rscg_examples_site/src/components/HomepageFeatures/index.js index 8052e4ba6..b693194eb 100644 --- a/v2/rscg_examples_site/src/components/HomepageFeatures/index.js +++ b/v2/rscg_examples_site/src/components/HomepageFeatures/index.js @@ -4,7 +4,7 @@ import styles from './styles.module.css'; const FeatureList = [ { -title: '278 Examples (16 from MSFT)', +title: '279 Examples (16 from MSFT)', Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, description: ( <> diff --git a/v2/rscg_examples_site/static/exports/RSCG.json b/v2/rscg_examples_site/static/exports/RSCG.json index fbc42e3d5..1f4eda312 100644 --- a/v2/rscg_examples_site/static/exports/RSCG.json +++ b/v2/rscg_examples_site/static/exports/RSCG.json @@ -2225,6 +2225,14 @@ "Source": "https://github.com/Atulin/AutoDbSet", "Category": "EntityFramework", "AddedOn": "2026-08-18T00:00:00" + }, + { + "Name": "ReflectionIT.DisposeGenerator", + "Link": "https://ignatandrei.github.io/RSCG_Examples/v2/docs/ReflectionIT.DisposeGenerator", + "NuGet": "https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/", + "Source": "https://github.com/sonnemaf/ReflectionIT.DisposeGenerator", + "Category": "Disposer", + "AddedOn": "2026-08-19T00:00:00" } ] } \ No newline at end of file diff --git a/v2/rscg_examples_site/static/exports/RSCG.xlsx b/v2/rscg_examples_site/static/exports/RSCG.xlsx index 57bad84aa850ce91a624a84d580e59665570a8b7..e33512de25ff9756232572e20d5987e5daa86d32 100644 GIT binary patch delta 906 zcmaD>IJu}kz?+#xgn@y9gQ3<^IQGOgp56O_JS9d327Vx2k)vOpUzDAcpP#K)k(<-o ze=zTm0Z&`J>ZjTz3j{(#qxlqkV;*uky}Go*H|A*K>&N;V7_Z;8`@e0?`}O7B^N)zT zm5E42tmhJt&?(i6k>0WN-{O|oX#K0^M?X55bk)f3sE?A82%4p>d6Vx2@A`~m6UuE} z^Mm9bhklZ|SYo4~W`EK*Fymz}cftSf9zR``B>H*hocrxB(EsE{s;t1qB1ylfeDmzk zMOtfvyt03M?)d+4!M<`a{*-(<=U<|o7Bi<N`0>hl$3vC#HY7ftt5|iqMg4#d2S@y8!{-l9Yc9J#_26OtN`ngx|F-w=Ka70w zr*4&e-~C%p&%au}?a$Bp|L{;}0Ec=){lgU+zz_#vZeXa#7o~1~WX!`TU}$7%WMH0V zo|I&6Y+{j`IQgH694E-N!5(w2+?cFts$PFE>o6!nj=tEZp|^^2ai0F(H&MTsj!m6> z;fUF`-CNIZJ1m#E-XOo{;sL(hZ&bc`|N7zZetrJzyQTAv7%#VBTkmM5%kj-@j@$k0 z9U|W^9L(Eo`60#Y?uo+^n^&mYWlMN<_IKRmvzdLT<-|U}7>~`GTYYWalFu(okJ-BR zP|5j*devZ={E7dac+T}s`+hJk?b}mvgJ(svEt)N6T`9iUaQ&rqwifr1%btf9DzCgV zOT;tVW1A4mzu6bOJ)YYbOkaB0KW3_Z_nynaS$ij4F)W?dG3D2#!$*a;$)m>9O}+UK ze*ps~f{B4a7)VDu=jWBA=9R>UZ1y$dX9NccIO$GKH8)`f2GHa==F*IBChstpQ9zAc zW(EnMegxRcH2FM})MP#jo=A{F^q4>>{Adi0mz-jKy`t2d;s9?(CJ_ci#sNnNQ1xD5 q6iOj<0a;F_ll4qxCih$LfE_l^LYgVaY_g-7=;R9)0&KPBAjJUf31Tt; delta 823 zcmbPS^sKNxz?+#xgn@y9gTZF4P%NKsht^&oPl=I%fgea$u=mJ0gD9A&JkIkYGB`=8v%_u{J$mObj4$GcNV z?J*0BmG9QV+OW*y6!2T!>_ufUh&J$DQlv)bREC4xO;coo>0ZU ze=0k_ugJ<1Y<`cxB<^JjDX_R;OT8S=Vv z?vl+uOFw-3@V_G9vP7@+CGl>(JuWt7T1U>^nKjSx#HtDQD?8@zyDE$7)l=~epXM=c zV%*Qj2lBaQ!1k6K1`G^dLYvPr9W!t`^fdN_lHpUMnzP5}99Ey1^ea|UM|Km#g1_JL zCFPk`TkoHrHS7O1z51|Zz50Lm|I5P+X8?y|dz_{BW@ZyC9 zLyO6JrgB0c3&l^L?70mL;=RD2mSUK^z(S^e>qOhU!v;KV>y--DtM2ZT77yH1@>1#v z6PNFQg>5PxKC|cOI`>`C4$GDDO=Ld$mn02eqW=b8EZndyjt$1F9W7CUS z2g{`T?%3%hZjQ^%G?`RLYg_G|+I9Q^> zy;Mu`U8bZ#de_~Xm*YFu<+3jkZ$I9t^Pg$j#PiCo0@lJ-NxYv->e3^V8Y^;5a!xOE zTGU!~YI5hbCwHTD>0=yZSL>Lfh6cJO*3=%-a z2(Wpwf`vK<$XHPLPxiMEvpQ(Zzz|=Qnp3Q=2O`nULX8B3Nem1^ljmD#f*p0*LYh%^ N@^cF{wg7XG3IH5JHx~c^ From bd9242b82811e065564a075c90eebdabe582bc1a Mon Sep 17 00:00:00 2001 From: Andrei Ignat Date: Wed, 19 Aug 2026 07:14:34 +0300 Subject: [PATCH 3/5] third --- .../ReflectionIT.DisposeGenerator.html | 59 ++++++++++++++++++ v2/book/list.html | 6 +- v2/book/pandocHTML.yaml | 1 + v2/docFind.json | 6 ++ .../sources/ReflectionIT.DisposeGenerator.zip | Bin 0 -> 1857 bytes 5 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 v2/book/examples/ReflectionIT.DisposeGenerator.html create mode 100644 v2/rscg_examples_site/static/sources/ReflectionIT.DisposeGenerator.zip diff --git a/v2/book/examples/ReflectionIT.DisposeGenerator.html b/v2/book/examples/ReflectionIT.DisposeGenerator.html new file mode 100644 index 000000000..a094f2948 --- /dev/null +++ b/v2/book/examples/ReflectionIT.DisposeGenerator.html @@ -0,0 +1,59 @@ + +

RSCG nr 279 : ReflectionIT.DisposeGenerator

+ +

Info

+Nuget : https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/ + +

You can find more details at : https://github.com/sonnemaf/ReflectionIT.DisposeGenerator

+ +

Author :Fons Sonnemans

+ +

Source: https://github.com/sonnemaf/ReflectionIT.DisposeGenerator

+ +

About

+ +Automatically implements the IDisposable pattern for classes that contain disposable fields. + +

+ How to use +

+

+ Add reference to the ReflectionIT.DisposeGenerator in the csproj +

+ + +

This was for me the starting code

+ +
+ I have coded the file Program.cs +
+ +
+ +
+ I have coded the file ConnectionDB.cs +
+ +
+

And here are the generated files

+ +
+ The file generated is IDisposableGeneratorDemo.DALDB.g.cs +
+ + +

+ You can download the code and this page as pdf from + + https://ignatandrei.github.io/RSCG_Examples/v2/docs/ReflectionIT.DisposeGenerator + +

+ + +

+ You can see the whole list at + + https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG + +

+ diff --git a/v2/book/list.html b/v2/book/list.html index 07f177f04..e62f28c0d 100644 --- a/v2/book/list.html +++ b/v2/book/list.html @@ -17,7 +17,7 @@

-This is the list of 278 RSCG with examples => +This is the list of 279 RSCG with examples =>

@@ -1138,6 +1138,10 @@

+ + + +
278 Atulin.AutoDbSet
279ReflectionIT.DisposeGenerator
diff --git a/v2/book/pandocHTML.yaml b/v2/book/pandocHTML.yaml index ad1a40133..1b8dd70fa 100644 --- a/v2/book/pandocHTML.yaml +++ b/v2/book/pandocHTML.yaml @@ -292,6 +292,7 @@ input-files: - examples/TinyBDD.MSTest.html - examples/Bennewitz.Ninja.AutoVersioning.html - examples/Atulin.AutoDbSet.html +- examples/ReflectionIT.DisposeGenerator.html # or you may use input-file: with a single value # defaults: diff --git a/v2/docFind.json b/v2/docFind.json index f8690632d..c2e276b50 100644 --- a/v2/docFind.json +++ b/v2/docFind.json @@ -1666,5 +1666,11 @@ "category": "EntityFramework", "href": "/RSCG_Examples/v2/docs/Atulin.AutoDbSet/", "body": "Package Description" + }, + { + "title": "ReflectionIT.DisposeGenerator", + "category": "Disposer", + "href": "/RSCG_Examples/v2/docs/ReflectionIT.DisposeGenerator/", + "body": "Package Description" } ] \ No newline at end of file diff --git a/v2/rscg_examples_site/static/sources/ReflectionIT.DisposeGenerator.zip b/v2/rscg_examples_site/static/sources/ReflectionIT.DisposeGenerator.zip new file mode 100644 index 0000000000000000000000000000000000000000..0bef01031570bf68fd1863d148d8f92e380b3ff2 GIT binary patch literal 1857 zcmZ`)c{CJS7$3}7vX8NjCZS|WmeC9$>ysr}r%WVCB8(VBNsM8T9uuRI-IP*9woD!+ z>ljORPV|Psgh#8Tm^an2oOir?&;93~`}@xME#G(Tths;?006)XcyG^eZ8CzSR&f9T z9zXzqk9})t<`W#K9_$xDcIXMn)PO{)gIPC*td;sn{sPUykvGoPTAmg^IFh|;5+n9P z9{F11Y5OOy$RVY9NncWmj*p)6Fn_J#^zzt5@XFdUHN>#kr64)GdJN%JoBwIQ|4Ehk zKy&q``XzAGR|(uaEAl3`QSSWOGWsBe zp?VG|T1Yr6R5p7P8hOZjo?Ji0HzftqZLseIB}!WA?$lJ*519ELGuHnvNG3 z?(>P1qwYv;)zM;$K~cU_q>n%|pr?^&GQe9Iq*ll&)_$(DtvPJBeFkNvB-oMZWB;PF zsp`(D?$tV3Fq9clBH`xvCH}H=MQ~;o^W75}UBcXal8r5;sjKk%;vW;GBtzs<%%O53 zKDA5Hf*|hV$#eM3(8%z5ysH1mbk!J%Pd|`f?8qENmL~ad>@upQ!qJ(pC+f?QU9$}n zRJpGC)xl|Ls&r7bHM;GA^QXS3PsZurKR;C`J5RPfOt?Y?d@M+>w0CrV^v%OM%XQUm z9Q(N9i(Yehhh;YBn{a`P`@szf4RQj#rwI0m@(_%z%#cFKXQ17txlE*(+W|pj!64hz z5VpbM8|EG|#RLR+d18Gq0cIxZp27YpWxkC@0&!uuapPE_&U0D_?8QsGaW>?Xm20TP z85b?37uKz}LuM2h;t|Zd17vYJiB-RlfemNfNm~YGFG_Vo`Kr4@O!X4!Led4=31916 z3InwI{9V#O6pj!qERTj7#P12i;S@84ZN*`D-QY}~8n;y{-AN+FiY6T{cX&~Yyd-pZ zR4v(2l2{SzG^<9-DeW)!5TLu4m7e}4%5%P09;S^kXzZ-NgE2AaNLp{SNmb#PHC1dh z&_p4M*Qt?pBo?e_;eAC1W)ZU58G$4EK553%Dod7mFLhvuV>4zN>TRZhk9|2;)3iH}_02p@;bq)Nv^s%}u4I-q zV)cVA;Q6gCrl79~oBwqX_3S=VmvWigKe^ntH+kSu5-;y1Q@ElZp7{2V)cV^bLpx!3 zIx1-KPr3&}CTt&{mfXwh`UE=GR;@gzq3*fQD0GWuJKOIGxZ1oLn<0u5003{Y{N2)? z!GS>-Up%qQwoyYQ&RgZ{?tB6VH!()4SHS4BSFV$Y2v?Hjx`bm(Ri~s?PI9ybT|97v zKzbbWk+skp6=oO}d2PR#R_Os`jq3E;B3k17{kuVM*ko$;>xxlkXiV-aHA04hR^@Rh zH4~vTdhavYTH$2kX)d_v?SP47V#Ui8qilqe29MXv@5i7X3ozX#~bRkm`4?!sRb==ah7iK6&#I-9--o?eq z7x5Ylosd_Zil#uKoJKX??D;HcXU)M00sQ~qXH(j?Hot-Gu$@NA7Ums0c)wu(9WdKV z?9_`}C8pWA{as?CGq+=RO86F*#c?) H1_1aM$@K0T literal 0 HcmV?d00001 From f8364275006b5e2bb15faef79db4d18ede0d9394 Mon Sep 17 00:00:00 2001 From: Andrei Ignat Date: Wed, 19 Aug 2026 11:31:29 +0300 Subject: [PATCH 4/5] Update ReflectionIT.DisposeGenerator.md --- .../ReflectionIT.DisposeGenerator.md | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/ReflectionIT.DisposeGenerator.md b/v2/rscg_examples_site/docs/RSCG-Examples/ReflectionIT.DisposeGenerator.md index f7e41b573..fe299cf86 100644 --- a/v2/rscg_examples_site/docs/RSCG-Examples/ReflectionIT.DisposeGenerator.md +++ b/v2/rscg_examples_site/docs/RSCG-Examples/ReflectionIT.DisposeGenerator.md @@ -49,7 +49,7 @@ Fons Sonnemans ## Original Readme :::note -# ReflectionIT.DisposeGenerator +### ReflectionIT.DisposeGenerator A source generator package that implements the dispose and async dispose patterns. @@ -57,19 +57,19 @@ A source generator package that implements the dispose and async dispose pattern - https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose - https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-disposeasync -## NuGet package +###### NuGet package | Package | Version | | ------ | ------ | | ReflectionIT.DisposeGenerator | [![NuGet](https://img.shields.io/nuget/v/ReflectionIT.DisposeGenerator)](https://www.nuget.org/packages/ReflectionIT.DisposeGenerator/) | -## Installation +###### Installation ```xml ``` -## Quick start +###### Quick start Annotate a **partial** class or struct with the `Disposable` attribute and mark disposable fields or properties with `Dispose`. @@ -95,7 +95,7 @@ public partial class LogWriter : IDisposable { The generator creates the dispose members for the annotated type, including `_isDisposed`, an `IsDisposed` property, and `ThrowIfDisposed()` by default. -## Requirements and diagnostics +###### Requirements and diagnostics - The annotated type must be `partial`. - `[Disposable]` can be applied to classes and structs. @@ -103,13 +103,13 @@ The generator creates the dispose members for the annotated type, including `_is - The generator emits `RITDG001` when a type annotated with `[Disposable]` is not declared `partial`. - Members annotated with `[Dispose]` or `[AsyncDispose]` must support the generated dispose call pattern. Otherwise the generated code can produce compiler errors. -### RITDG001 +######### RITDG001 `RITDG001`: Type `'{typeName}'` is annotated with `[Disposable]` and must be declared partial for ReflectionIT.DisposeGenerator to generate code. -## Attribute reference +###### Attribute reference -### `DisposableAttribute` +######### `DisposableAttribute` | Property | Default | Description | | --- | --- | --- | @@ -120,20 +120,20 @@ The generator creates the dispose members for the annotated type, including `_is | `IsThreadSafe` | `false` | Uses thread-safe disposal state transitions via `Interlocked.CompareExchange`. | | `HasUnmanagedResources` | `false` | Adds a finalizer and `ReleaseUnmanagedResources()` partial method support. | -### `DisposeAttribute` +######### `DisposeAttribute` | Property | Default | Description | | --- | --- | --- | | `SetToNull` | `false` | Sets the annotated field or property to `null` after disposal. | -### `AsyncDisposeAttribute` +######### `AsyncDisposeAttribute` | Property | Default | Description | | --- | --- | --- | | `SetToNull` | `false` | Sets the annotated field or property to `null` after asynchronous disposal. | | `ConfigureAwait` | `true` | Controls the `ConfigureAwait(...)` value used for generated async disposal calls. | -## What gets generated +###### What gets generated Depending on the options and the annotated members, the generator can create: @@ -147,7 +147,7 @@ Depending on the options and the annotated members, the generator can create: - a finalizer - `ReleaseUnmanagedResources()` -## Recommended use of `ThrowIfDisposed` +###### Recommended use of `ThrowIfDisposed` Call `ThrowIfDisposed()` at the start of public instance members that depend on resources managed by the generated dispose pattern. The generated method uses the generated `IsDisposed` property, so derived types can customize disposed-state checks through `IsDisposed` instead of overriding `ThrowIfDisposed()`. @@ -171,7 +171,7 @@ public partial class LogWriter : IDisposable { This helps fail fast with an `ObjectDisposedException` when the instance is used after it has been disposed. -## `SetToNull` usage +###### `SetToNull` usage Use `SetToNull` when the property or field should be set to `null` after disposal. @@ -243,7 +243,7 @@ partial class LogWriter } ``` -## Async dispose +###### Async dispose Use `AsyncDispose` for fields or properties that support `DisposeAsync()`. @@ -316,7 +316,7 @@ partial class LogWriter } ``` -## Implement the dispose pattern for a derived class +###### Implement the dispose pattern for a derived class A class derived from a class that already implements `IDisposable` should not implement `IDisposable` again, because the base implementation of `IDisposable.Dispose` is inherited by derived classes. @@ -380,7 +380,7 @@ partial class SecondLogWriter } ``` -## Unmanaged resources +###### Unmanaged resources Set `HasUnmanagedResources = true` to include unmanaged resource cleanup support. Then implement the partial method `ReleaseUnmanagedResources()`, which releases the unmanaged resource. @@ -469,7 +469,7 @@ partial class LogWriterWithAnExtraIntPtr } ``` -## Thread-safe disposal +###### Thread-safe disposal Use `IsThreadSafe = true` to ensure thread-safe disposal via `Interlocked.CompareExchange`. @@ -534,17 +534,17 @@ partial class LogWriter } ``` -## Troubleshooting +###### Troubleshooting -### Why do I get `RITDG001`? +######### Why do I get `RITDG001`? The type marked with `[Disposable]` is not declared `partial`. Add the `partial` keyword to the class or struct declaration. -### Why do I get compiler errors for `Dispose()` or `DisposeAsync()` on an annotated member? +######### Why do I get compiler errors for `Dispose()` or `DisposeAsync()` on an annotated member? The generator emits calls to `Dispose()` for `[Dispose]` members and `DisposeAsync()` for `[AsyncDispose]` members. Make sure the annotated member supports the corresponding API. -### Why was no code generated? +######### Why was no code generated? Common reasons: @@ -552,9 +552,9 @@ Common reasons: - no fields or properties were annotated with `[Dispose]` or `[AsyncDispose]` - the type only has invalid attribute usage that prevents successful compilation -## License +###### License -This project is licensed under the MIT License. See the [LICENSE](LICENSE.txt) file for details. +This project is licensed under the MIT License. See the [LICENSE](https://github.com/sonnemaf/ReflectionIT.DisposeGenerator/LICENSE.md) file for details. ::: From 491ca642c55c4a60982558196762caf8b0797665 Mon Sep 17 00:00:00 2001 From: Andrei Ignat Date: Wed, 19 Aug 2026 14:16:30 +0300 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- v2/rscg_examples/ReflectionIT.DisposeGenerator/description.json | 2 +- .../ReflectionIT.DisposeGenerator/src/IDisp/ConnectionDB.cs | 2 -- .../ReflectionIT.DisposeGenerator/src/IDisp/Program.cs | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/v2/rscg_examples/ReflectionIT.DisposeGenerator/description.json b/v2/rscg_examples/ReflectionIT.DisposeGenerator/description.json index 2c1b59530..7827615f6 100644 --- a/v2/rscg_examples/ReflectionIT.DisposeGenerator/description.json +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/description.json @@ -11,7 +11,7 @@ "data":{ "goodFor":["Automatically implements the IDisposable pattern for classes that contain disposable fields."], "csprojDemo":"IDisp.csproj", - "csFiles":["Program.cs","ConnectionDB.cs"], + "csFiles":["Program.cs","DALDB.cs","ConnectionDB.cs"], "excludeDirectoryGenerated":[""], "includeAdditionalFiles":[""] }, diff --git a/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/ConnectionDB.cs b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/ConnectionDB.cs index f730082e7..2c24c9fc3 100644 --- a/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/ConnectionDB.cs +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/ConnectionDB.cs @@ -1,5 +1,3 @@ -using ReflectionIT.DisposeGenerator.Attributes; - namespace IDisposableGeneratorDemo; class ConnectionDB : IDisposable { diff --git a/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/Program.cs b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/Program.cs index 601e1dcde..bcce4b491 100644 --- a/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/Program.cs +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/Program.cs @@ -1,5 +1,5 @@ using IDisposableGeneratorDemo; -//https://github.com/benutomo-dev/RoslynComponents +// https://github.com/sonnemaf/ReflectionIT.DisposeGenerator using (var db = new DALDB()) { Console.WriteLine("before releasing");