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/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/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/ReflectionIT.DisposeGenerator/description.json b/v2/rscg_examples/ReflectionIT.DisposeGenerator/description.json new file mode 100644 index 000000000..7827615f6 --- /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","DALDB.cs","ConnectionDB.cs"], + "excludeDirectoryGenerated":[""], + "includeAdditionalFiles":[""] + }, + "links":{ + "blog":"", + "video":"" + } +} \ No newline at end of file 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/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..2c24c9fc3 --- /dev/null +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/ConnectionDB.cs @@ -0,0 +1,13 @@ +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..bcce4b491 --- /dev/null +++ b/v2/rscg_examples/ReflectionIT.DisposeGenerator/src/IDisp/Program.cs @@ -0,0 +1,7 @@ +using IDisposableGeneratorDemo; +// https://github.com/sonnemaf/ReflectionIT.DisposeGenerator +using (var db = new DALDB()) +{ + Console.WriteLine("before releasing"); +} +Console.WriteLine("after releasing"); \ 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..fe299cf86 --- /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](https://github.com/sonnemaf/ReflectionIT.DisposeGenerator/LICENSE.md) 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 57bad84aa..e33512de2 100644 Binary files a/v2/rscg_examples_site/static/exports/RSCG.xlsx and b/v2/rscg_examples_site/static/exports/RSCG.xlsx differ 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 000000000..0bef01031 Binary files /dev/null and b/v2/rscg_examples_site/static/sources/ReflectionIT.DisposeGenerator.zip differ