Category: .NET

RSCG – CredFetoEnum

RSCG – CredFetoEnum
 
 

name CredFetoEnum
nuget https://www.nuget.org/packages/Credfeto.Enumeration.Source.Generation/
link https://github.com/credfeto/credfeto-enum-source-generation
author Mark Ridgwell

Enum / description to string

 

This is how you can use CredFetoEnum .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Credfeto.Enumeration.Source.Generation" Version="1.1.0.138" OutputItemType="Analyzer">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using EnumClassDemo;
Console.WriteLine(Colors.None.GetName());
Console.WriteLine(Colors.None.GetDescription());


using System.ComponentModel;

namespace EnumClassDemo;

public enum Colors
{
    [Description("This should be never seen")]
    None =0,
    Red,
    Green,
    Blue,
}


 

The code that is generated is

using System;
using System.CodeDom.Compiler;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace EnumClassDemo;

[GeneratedCode(tool: "Credfeto.Enumeration.Source.Generation.EnumGenerator", version: "1.1.0.138+a4e45a10ca3da5e916ae17843913bfff8c33cdef")]
public static class ColorsGeneratedExtensions
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static string GetName(this Colors value)
    {
        return value switch
        {
            Colors.None => nameof(Colors.None),
            Colors.Red => nameof(Colors.Red),
            Colors.Green => nameof(Colors.Green),
            Colors.Blue => nameof(Colors.Blue),
            _ => ThrowInvalidEnumMemberException(value: value)
        };
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static string GetDescription(this Colors value)
    {
        return value switch
        {
            Colors.None => "This should be never seen",
            _ => GetName(value)
        };
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool IsDefined(this Colors value)
    {
        return value is Colors.None or Colors.Red or Colors.Green or Colors.Blue;
    }

    public static string ThrowInvalidEnumMemberException(this Colors value)
    {
        #if NET7_0_OR_GREATER
        throw new UnreachableException(message: "Colors: Unknown enum member");
        #else
        throw new ArgumentOutOfRangeException(nameof(value), actualValue: value, message: "Unknown enum member");
        #endif
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/CredFetoEnum

RSCG – IDisposableGenerator

RSCG – IDisposableGenerator
 
 

name IDisposableGenerator
nuget https://www.nuget.org/packages/IDisposableGenerator/
link https://github.com/Elskom/IDisposableGenerator
author Els_kom Official Organization

Generating disposable

 

This is how you can use IDisposableGenerator .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
	 <PropertyGroup>
        <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
        <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
    </PropertyGroup>
	 <ItemGroup>
	   <PackageReference Include="IDisposableGenerator" Version="1.1.1" OutputItemType="Analyzer"  >
	     <PrivateAssets>all</PrivateAssets>
	     <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
	   </PackageReference>
	 </ItemGroup>

</Project>


The code that you will use is


using IDisposableGeneratorDemo;
//https://github.com/benutomo-dev/RoslynComponents
using (var db = new DALDB())
{
    Console.WriteLine("before releasing");
}
Console.WriteLine("after releasing");


namespace IDisposableGeneratorDemo;

[IDisposableGenerator.GenerateDispose(false)]
partial class DALDB :IDisposable
{
    [IDisposableGenerator.DisposeField(true)]
    private ConnectionDB cn;
    [IDisposableGenerator.DisposeField(true)] 
    private ConnectionDB cn1;

    public DALDB()
    {
        cn = new ConnectionDB();
        cn1=new ConnectionDB();
    }

}



namespace IDisposableGeneratorDemo;

class ConnectionDB : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("disposing connectiondb");
    }
}


 

The code that is generated is

// <autogenerated/>
namespace IDisposableGeneratorDemo;

internal partial class DALDB : IDisposable
{
    private bool isDisposed;

    internal bool IsOwned { get; set; }

    /// <summary>
    /// Cleans up the resources used by <see cref="DALDB"/>.
    /// </summary>
    public void Dispose() => this.Dispose(true);

    private void Dispose(bool disposing)
    {
        if (!this.isDisposed && disposing)
        {
            if (this.IsOwned)
            {
                this.cn?.Dispose();
                this.cn = null;
                this.cn1?.Dispose();
                this.cn1 = null;
            }
            this.isDisposed = true;
        }
    }
}

// <autogenerated/>
#pragma warning disable SA1636, 8618
namespace IDisposableGenerator
{
    using System;

    // used only by a source generator to generate Dispose() and Dispose(bool).
    [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
    internal class CallOnDisposeAttribute : Attribute
    {
        public CallOnDisposeAttribute()
        {
        }
    }

    // used only by a source generator to generate Dispose() and Dispose(bool).
    [AttributeUsage(AttributeTargets.Event | AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
    internal class DisposeFieldAttribute : Attribute
    {
        public DisposeFieldAttribute(bool owner)
        {
        }
    }

    // used only by a source generator to generate Dispose() and Dispose(bool).
    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
    internal class GenerateDisposeAttribute : Attribute
    {
        public GenerateDisposeAttribute(bool stream)
        {
        }
    }

    // used only by a source generator to generate Dispose() and Dispose(bool).
    [AttributeUsage(AttributeTargets.Event | AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
    internal class NullOnDisposeAttribute : Attribute
    {
        public NullOnDisposeAttribute()
        {
        }
    }
}
#pragma warning restore SA1636, 8618

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/IDisposableGenerator

RSCG – Meziantou.Polyfill

RSCG – Meziantou.Polyfill
 
 

name Meziantou.Polyfill
nuget https://www.nuget.org/packages/Meziantou.Polyfill/
link https://www.meziantou.net/polyfills-in-dotnet-to-ease-multi-targeting.htm
author Gérald Barré

Generating polyfills that you can see source without de-compiling

 

This is how you can use Meziantou.Polyfill .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>net7.0;netstandard2.0</TargetFrameworks>
	  <LangVersion>latest</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Meziantou.Polyfill" Version="1.0.28">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>

	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


// See https://aka.ms/new-console-template for more information
System.Console.WriteLine("Hello, World!");



using System.Diagnostics;
using System.Threading.Tasks;

namespace Meziantou.PolyfillDemo
{

    internal class StartProcess
    {
        static async Task StartNotepad()
        {
            await Task.Delay(1000);
            var process = Process.Start("notepad.exe");

#if NET6_0_OR_GREATER
           await process.WaitForExitAsync();
#else
            process.WaitForExit();
#endif
            
        }
        static async Task StartNotepadPolyFill()
        {
            await Task.Delay(1000);
            var process = Process.Start("notepad.exe");
            //do remove nuget Meziantou.Polyfill - this line will not be ok.
            await process.WaitForExitAsync();

        }

    }
}

 

The code that is generated is

// IncludedMembers: <null>
// ExcludedMembers: <null>
// System.Collections.Immutable.ImmutableArray`1: True
// System.Memory`1: True
// System.Net.Http.HttpContent: True
// System.ReadOnlyMemory`1: True
// System.ReadOnlySpan`1: True
// System.Span`1: True
// System.Threading.Tasks.ValueTask: True
// System.Threading.Tasks.ValueTask`1: True
//
// M:System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd``1(`0,System.Func{`0,``0,`1},``0): False
// M:System.Collections.Generic.CollectionExtensions.GetValueOrDefault``2(System.Collections.Generic.IReadOnlyDictionary{``0,``1},``0): False
// M:System.Collections.Generic.CollectionExtensions.GetValueOrDefault``2(System.Collections.Generic.IReadOnlyDictionary{``0,``1},``0,``1): False
// M:System.Collections.Generic.KeyValuePair`2.Deconstruct(`0@,`1@): False
// M:System.Collections.Generic.Queue`1.TryDequeue(`0@): False
// M:System.Collections.Immutable.ImmutableArray`1.AsSpan(System.Int32,System.Int32): False
// M:System.Collections.Immutable.ImmutableArray`1.AsSpan(System.Range): False
// M:System.Diagnostics.Process.WaitForExitAsync(System.Threading.CancellationToken): False
// M:System.IO.Stream.Read(System.Span{System.Byte}): False
// M:System.IO.Stream.ReadAsync(System.Memory{System.Byte},System.Threading.CancellationToken): False
// M:System.IO.Stream.ReadAtLeast(System.Span{System.Byte},System.Int32,System.Boolean): False
// M:System.IO.Stream.ReadAtLeastAsync(System.Memory{System.Byte},System.Int32,System.Boolean,System.Threading.CancellationToken): False
// M:System.IO.Stream.Write(System.ReadOnlySpan{System.Byte}): False
// M:System.IO.Stream.WriteAsync(System.ReadOnlyMemory{System.Byte},System.Threading.CancellationToken): False
// M:System.IO.StreamReader.ReadLineAsync(): False
// M:System.IO.StreamReader.ReadLineAsync(System.Threading.CancellationToken): False
// M:System.IO.TextReader.ReadAsync(System.Memory{System.Char},System.Threading.CancellationToken): False
// M:System.IO.TextReader.ReadToEndAsync(System.Threading.CancellationToken): False
// M:System.IO.TextWriter.WriteAsync(System.ReadOnlyMemory{System.Char},System.Threading.CancellationToken): False
// M:System.Linq.Enumerable.DistinctBy``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1}): False
// M:System.Linq.Enumerable.DistinctBy``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1},System.Collections.Generic.IEqualityComparer{``1}): False
// M:System.Linq.Enumerable.MaxBy``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1}): False
// M:System.Linq.Enumerable.MaxBy``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1},System.Collections.Generic.IComparer{``1}): False
// M:System.Linq.Enumerable.MinBy``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1}): False
// M:System.Linq.Enumerable.MinBy``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1},System.Collections.Generic.IComparer{``1}): False
// M:System.Linq.Enumerable.OrderDescending``1(System.Collections.Generic.IEnumerable{``0}): False
// M:System.Linq.Enumerable.OrderDescending``1(System.Collections.Generic.IEnumerable{``0},System.Collections.Generic.IComparer{``0}): False
// M:System.Linq.Enumerable.Order``1(System.Collections.Generic.IEnumerable{``0}): False
// M:System.Linq.Enumerable.Order``1(System.Collections.Generic.IEnumerable{``0},System.Collections.Generic.IComparer{``0}): False
// M:System.Linq.Enumerable.ToHashSet``1(System.Collections.Generic.IEnumerable{``0}): False
// M:System.Linq.Enumerable.ToHashSet``1(System.Collections.Generic.IEnumerable{``0},System.Collections.Generic.IEqualityComparer{``0}): False
// M:System.Linq.Enumerable.Zip``2(System.Collections.Generic.IEnumerable{``0},System.Collections.Generic.IEnumerable{``1}): False
// M:System.MemoryExtensions.AsSpan(System.String,System.Int32,System.Int32): False
// M:System.MemoryExtensions.Contains``1(System.ReadOnlySpan{``0},``0): False
// M:System.MemoryExtensions.Contains``1(System.Span{``0},``0): False
// M:System.Net.Http.HttpContent.CopyTo(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken): False
// M:System.Net.Http.HttpContent.CopyToAsync(System.IO.Stream): False
// M:System.Net.Http.HttpContent.CopyToAsync(System.IO.Stream,System.Net.TransportContext): False
// M:System.Net.Http.HttpContent.CopyToAsync(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken): False
// M:System.Net.Http.HttpContent.CopyToAsync(System.IO.Stream,System.Threading.CancellationToken): False
// M:System.Net.Http.HttpContent.ReadAsStream(System.Threading.CancellationToken): False
// M:System.Net.Http.HttpContent.ReadAsStream: False
// M:System.String.Contains(System.Char): False
// M:System.String.Contains(System.Char,System.StringComparison): False
// M:System.String.Contains(System.String,System.StringComparison): False
// M:System.String.CopyTo(System.Span{System.Char}): False
// M:System.String.EndsWith(System.Char): False
// M:System.String.GetHashCode(System.StringComparison): False
// M:System.String.IndexOf(System.Char,System.StringComparison): False
// M:System.String.Replace(System.String,System.String,System.StringComparison): False
// M:System.String.ReplaceLineEndings(System.String): False
// M:System.String.ReplaceLineEndings: False
// M:System.String.Split(System.Char,System.Int32,System.StringSplitOptions): False
// M:System.String.Split(System.Char,System.StringSplitOptions): False
// M:System.String.StartsWith(System.Char): False
// M:System.String.TryCopyTo(System.Span{System.Char}): False
// M:System.Text.Encoding.GetString(System.ReadOnlySpan{System.Byte}): False
// M:System.Text.StringBuilder.Append(System.ReadOnlyMemory{System.Char}): False
// M:System.Text.StringBuilder.Append(System.ReadOnlySpan{System.Char}): False
// M:System.Text.StringBuilder.AppendJoin(System.Char,System.Object[]): False
// M:System.Text.StringBuilder.AppendJoin(System.Char,System.String[]): False
// M:System.Text.StringBuilder.AppendJoin(System.String,System.Object[]): False
// M:System.Text.StringBuilder.AppendJoin(System.String,System.String[]): False
// M:System.Text.StringBuilder.AppendJoin``1(System.Char,System.Collections.Generic.IEnumerable{``0}): False
// M:System.Text.StringBuilder.AppendJoin``1(System.String,System.Collections.Generic.IEnumerable{``0}): False
// M:System.Threading.CancellationTokenSource.CancelAsync: True
// M:System.Threading.Tasks.Task.WaitAsync(System.Threading.CancellationToken): False
// T:System.Collections.Generic.PriorityQueue`2: False
// T:System.Collections.Generic.ReferenceEqualityComparer: False
// T:System.Diagnostics.CodeAnalysis.AllowNullAttribute: False
// T:System.Diagnostics.CodeAnalysis.DisallowNullAttribute: False
// T:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute: False
// T:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute: False
// T:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute: False
// T:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes: False
// T:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute: False
// T:System.Diagnostics.CodeAnalysis.MaybeNullAttribute: False
// T:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute: False
// T:System.Diagnostics.CodeAnalysis.MemberNotNullAttribute: False
// T:System.Diagnostics.CodeAnalysis.MemberNotNullWhenAttribute: False
// T:System.Diagnostics.CodeAnalysis.NotNullAttribute: False
// T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute: False
// T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute: False
// T:System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute: False
// T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute: False
// T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute: False
// T:System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute: False
// T:System.Diagnostics.CodeAnalysis.StringSyntaxAttribute: False
// T:System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute: False
// T:System.Diagnostics.CodeAnalysis.UnscopedRefAttribute: False
// T:System.Diagnostics.StackTraceHiddenAttribute: False
// T:System.HashCode: False
// T:System.Index: False
// T:System.Net.Http.ReadOnlyMemoryContent: False
// T:System.Range: False
// T:System.Runtime.CompilerServices.AsyncMethodBuilderAttribute: False
// T:System.Runtime.CompilerServices.CallerArgumentExpressionAttribute: False
// T:System.Runtime.CompilerServices.CollectionBuilderAttribute: True
// T:System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute: False
// T:System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute: False
// T:System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute: False
// T:System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute: False
// T:System.Runtime.CompilerServices.IsExternalInit: False
// T:System.Runtime.CompilerServices.ModuleInitializerAttribute: False
// T:System.Runtime.CompilerServices.RequiredMemberAttribute: False
// T:System.Runtime.CompilerServices.SkipLocalsInitAttribute: False
// T:System.Runtime.CompilerServices.TupleElementNamesAttribute: False
// T:System.Runtime.InteropServices.SuppressGCTransitionAttribute: False
// T:System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute: False
// T:System.Runtime.Versioning.ObsoletedOSPlatformAttribute: False
// T:System.Runtime.Versioning.RequiresPreviewFeaturesAttribute: False
// T:System.Runtime.Versioning.SupportedOSPlatformAttribute: False
// T:System.Runtime.Versioning.SupportedOSPlatformGuardAttribute: False
// T:System.Runtime.Versioning.TargetPlatformAttribute: False
// T:System.Runtime.Versioning.UnsupportedOSPlatformAttribute: False
// T:System.Runtime.Versioning.UnsupportedOSPlatformGuardAttribute: False
// T:System.Threading.Tasks.TaskToAsyncResult: True
// T:System.ValueTuple: False
// T:System.ValueTuple`1: False
// T:System.ValueTuple`2: False
// T:System.ValueTuple`3: False
// T:System.ValueTuple`4: False
// T:System.ValueTuple`5: False
// T:System.ValueTuple`6: False
// T:System.ValueTuple`7: False
// T:System.ValueTuple`8: False
// T:System.ITupleInternal: False

// <auto-generated/>
#pragma warning disable
#nullable enable annotations
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;
using System;

static partial class PolyfillExtensions
{
    public static async Task WaitForExitAsync(this Process target, CancellationToken cancellationToken = default)
    {
        // https://source.dot.net/#System.Diagnostics.Process/System/Diagnostics/Process.cs,b6a5b00714a61f06
        // Because the process has already started by the time this method is called,
        // we're in a race against the process to set up our exit handlers before the process
        // exits. As a result, there are several different flows that must be handled:
        //
        // CASE 1: WE ENABLE EVENTS
        // This is the "happy path". In this case we enable events.
        //
        // CASE 1.1: PROCESS EXITS OR IS CANCELED AFTER REGISTERING HANDLER
        // This case continues the "happy path". The process exits or waiting is canceled after
        // registering the handler and no special cases are needed.
        //
        // CASE 1.2: PROCESS EXITS BEFORE REGISTERING HANDLER
        // It's possible that the process can exit after we enable events but before we reigster
        // the handler. In that case we must check for exit after registering the handler.
        //
        //
        // CASE 2: PROCESS EXITS BEFORE ENABLING EVENTS
        // The process may exit before we attempt to enable events. In that case EnableRaisingEvents
        // will throw an exception like this:
        //     System.InvalidOperationException : Cannot process request because the process (42) has exited.
        // In this case we catch the InvalidOperationException. If the process has exited, our work
        // is done and we return. If for any reason (now or in the future) enabling events fails
        // and the process has not exited, bubble the exception up to the user.
        //
        //
        // CASE 3: USER ALREADY ENABLED EVENTS
        // In this case the user has already enabled raising events. Re-enabling events is a no-op
        // as the value hasn't changed. However, no-op also means that if the process has already
        // exited, EnableRaisingEvents won't throw an exception.
        //
        // CASE 3.1: PROCESS EXITS OR IS CANCELED AFTER REGISTERING HANDLER
        // (See CASE 1.1)
        //
        // CASE 3.2: PROCESS EXITS BEFORE REGISTERING HANDLER
        // (See CASE 1.2)
        if (!target.HasExited)
        {
            // Early out for cancellation before doing more expensive work
            cancellationToken.ThrowIfCancellationRequested();
        }
        try
        {
            // CASE 1: We enable events
            // CASE 2: Process exits before enabling events (and throws an exception)
            // CASE 3: User already enabled events (no-op)
            target.EnableRaisingEvents = true;
        }
        catch (InvalidOperationException)
        {
            // CASE 2: If the process has exited, our work is done, otherwise bubble the
            // exception up to the user
            if (target.HasExited)
            {
                return;
            }
            throw;
        }
        var tcs = new TaskCompletionSourceWithCancellation<bool>();
        void Handler(object? s, EventArgs e) => tcs.TrySetResult(true);
        target.Exited += Handler;
        try
        {
            if (target.HasExited)
            {
                // CASE 1.2 & CASE 3.2: Handle race where the process exits before registering the handler
                return;
            }
            // CASE 1.1 & CASE 3.1: Process exits or is canceled here
            await tcs.WaitWithCancellationAsync(cancellationToken).ConfigureAwait(false);
        }
        finally
        {
            target.Exited -= Handler;
        }

        target.WaitForExit();
    }

    private sealed class TaskCompletionSourceWithCancellation<T> : TaskCompletionSource<T>
    {
        private CancellationToken _cancellationToken;
        public TaskCompletionSourceWithCancellation() : base(TaskCreationOptions.RunContinuationsAsynchronously)
        {
        }
        private void OnCancellation()
        {
            TrySetCanceled(_cancellationToken);
        }
#if NETCOREAPP3_1_OR_GREATER
        public async ValueTask<T> WaitWithCancellationAsync(CancellationToken cancellationToken)
        {
            _cancellationToken = cancellationToken;
            await using (cancellationToken.UnsafeRegister(s => ((TaskCompletionSourceWithCancellation<T>)s!).OnCancellation(), this))
            {
                return await Task.ConfigureAwait(false);
            }
        }
#else
        public async Task<T> WaitWithCancellationAsync(CancellationToken cancellationToken)
        {
            _cancellationToken = cancellationToken;
            using (cancellationToken.Register(s => ((TaskCompletionSourceWithCancellation<T>)s!).OnCancellation(), this))
            {
                return await Task.ConfigureAwait(false);
            }
        }
#endif
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Meziantou.Polyfill

RSCG – DisposableHelpers

RSCG – DisposableHelpers
 
 

name DisposableHelpers
nuget https://www.nuget.org/packages/DisposableHelpers/
link https://github.com/Kiryuumaru/DisposableHelpers
author Clynt Neiko Rupinta

Generating boilerplate for thread safe Dispose

 

This is how you can use DisposableHelpers .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

 	 <PropertyGroup>
        <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
        <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
    </PropertyGroup>

 	 <ItemGroup>
 	   <PackageReference Include="DisposableHelpers" Version="1.1.16" />
 	 </ItemGroup>

</Project>


The code that you will use is


using IDisposableGeneratorDemo;
//https://github.com/benutomo-dev/RoslynComponents
using (var db = new DALDB())
{
    Console.WriteLine("before releasing");
}
Console.WriteLine("after releasing");


using DisposableHelpers.Attributes;
using System.Resources;

namespace IDisposableGeneratorDemo;


[Disposable]
partial class DALDB 
{
    
    private readonly ConnectionDB cn;
    private readonly ConnectionDB cn1;

    public DALDB()
    {
        cn = new ConnectionDB();
        cn1=new ConnectionDB();
    }

    protected void Dispose(bool disposing)
    {
        if (disposing)
        {
            cn.Dispose();
            cn1.Dispose();
        }
    }
}



namespace IDisposableGeneratorDemo;

class ConnectionDB : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("disposing connectiondb");
    }
}


 

The code that is generated is

#pragma warning disable CS0436
#nullable enable

namespace Benutomo
{
    /// <summary>
    /// 指定したクラスに破棄(<see cref=""System.IDisposable"" />,<see cref=""System.IAsyncDisposable"" />)をサポートするメンバを破棄する<see cref=""System.IDisposable.Dispose"" />メソッドおよび<see cref=""System.IAsyncDisposable.DisposeAsync"" />メソッド(当該クラスに<see cref=""System.IAsyncDisposable"" />インターフェイスが含まれている場合のみ)を自動実装する。
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Class)]
    internal class AutomaticDisposeImplAttribute : global::System.Attribute
    {
        /// <summary>
        /// 自動破棄実装の既定動作を設定する。
        /// </summary>
        public AutomaticDisposeImplMode Mode { get; set; }
    }
}
#pragma warning disable CS0436
#nullable enable

namespace Benutomo
{
    /// <summary>
    /// 破棄(<see cref=""System.IDisposable"" />,<see cref=""System.IAsyncDisposable"" />)をサポートするメンバを自動実装Disposeの対象とすることに関する振る舞いの指定。
    /// </summary>
    internal enum AutomaticDisposeImplMode
    {
        /// <summary>
        /// <see cref=""System.IDisposable"" />,<see cref=""System.IAsyncDisposable"" />を継承する型を持つメンバは暗黙的に自動Dispose呼び出しの対象となる。
        /// </summary>
        Implicit,

        /// <summary>
        /// <see cref=""System.IDisposable"" />,<see cref=""System.IAsyncDisposable"" />を継承する型を持つメンバは自動Dispose呼び出しの対象となる。
        /// </summary>
        Explicit,
    }
}
#pragma warning disable CS0436
#nullable enable

namespace Benutomo
{
    /// <summary>
    /// このメンバに対して、<see cref=""System.IDisposable.Dispose"" />メソッドまたは<see cref=""System.IAsyncDisposable.DisposeAsync"" />メソッドの自動呼出しは行いません。このオブジェクトで破棄するのが不適当であるかユーザ自身が<see cref=""System.IDisposable.Dispose"" />メソッドまたは<see cref=""System.IAsyncDisposable.DisposeAsync"" />メソッドの呼び出しを実装するメンバです。
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property)]
    internal class DisableAutomaticDisposeAttribute : global::System.Attribute
    {
    }
}
#pragma warning disable CS0436
#nullable enable

namespace Benutomo
{
    /// <summary>
    /// このオブジェクトの破棄と同時に自動的に<see cref=""System.IDisposable.Dispose"" />メソッドまたは<see cref=""System.IAsyncDisposable.DisposeAsync"" />メソッドを呼び出します。
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property)]
    internal class EnableAutomaticDisposeAttribute : global::System.Attribute
    {
        public EnableAutomaticDisposeAttribute() { }

        /// <summary>
        /// このオブジェクトの破棄と同時に自動的に<see cref=""System.IDisposable.Dispose"" />メソッドまたは<see cref=""System.IAsyncDisposable.DisposeAsync"" />メソッドを呼び出します。
        /// </summary>
        /// <param name=""linkedMembers"">このメンバの破棄に連動して破棄されるメンバ(ここで列挙されたメンバはEnable/DisableAutomaticDispose属性を省略可能)</param>
        public EnableAutomaticDisposeAttribute(params string[] dependencyMembers) { }
    }
}
#nullable enable
#pragma warning disable CS0612,CS0618,CS0619
namespace IDisposableGeneratorDemo
{
    partial class DALDB // This is implementation class by AutomaticDisposeImpl.
    {
        [global::System.ComponentModel.Browsable(false)]
        [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
        [global::System.Obsolete("AutomaticDisposeImplによって生成されたフィールドです。一般のコードから参照してはいけません。")]
        private const int __generator_internal_BeNotInitiatedAnyDispose = 0;
        [global::System.ComponentModel.Browsable(false)]
        [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
        [global::System.Obsolete("AutomaticDisposeImplによって生成されたフィールドです。一般のコードから参照してはいけません。")]
        private const int __generator_internal_InitiatedSyncDispose  = 1;
        [global::System.ComponentModel.Browsable(false)]
        [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
        [global::System.Obsolete("AutomaticDisposeImplによって生成されたフィールドです。一般のコードから参照してはいけません。")]
        private const int __generator_internal_InitiatedAsyncDispose = 2;
        [global::System.ComponentModel.Browsable(false)]
        [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
        [global::System.Obsolete("AutomaticDisposeImplによって生成されたフィールドです。一般のコードから参照してはいけません。")]
        private const int __generator_internal_DisposeAlreadyCompleted = 9;
        [global::System.ComponentModel.Browsable(false)]
        [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
        [global::System.Obsolete("AutomaticDisposeImplによって生成されたフィールドです。一般のコードから参照してはいけません。")]
        private int __generator_internal_disposeState = __generator_internal_BeNotInitiatedAnyDispose;

        public bool IsDisposed => (global::System.Threading.Thread.VolatileRead(ref __generator_internal_disposeState) != __generator_internal_BeNotInitiatedAnyDispose);

        [global::System.ComponentModel.Browsable(false)]
        [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
        [global::System.Obsolete("AutomaticDisposeImplによって生成されたフィールドです。一般のコードから参照してはいけません。")]
        private int __generator_internal_managedObjectDisposeState = 0;

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                var managedObjectDisposeState = global::System.Threading.Interlocked.Exchange(ref __generator_internal_managedObjectDisposeState, 1);
                if (managedObjectDisposeState == 0)
                {
                    try
                    {
                        (this.cn as global::System.IDisposable)?.Dispose();
                    }
                    catch (global::System.Exception ex)
                    {
                        global::System.Diagnostics.Debug.Fail($"Caught an exception in the cn.Dispose() calling. Message=\"{ex.Message}\"");
                    }
                    try
                    {
                        (this.cn1 as global::System.IDisposable)?.Dispose();
                    }
                    catch (global::System.Exception ex)
                    {
                        global::System.Diagnostics.Debug.Fail($"Caught an exception in the cn1.Dispose() calling. Message=\"{ex.Message}\"");
                    }
                }
            }
        }

        public void Dispose()
        {
            var dispose_state = global::System.Threading.Interlocked.CompareExchange(ref __generator_internal_disposeState, __generator_internal_InitiatedSyncDispose, __generator_internal_BeNotInitiatedAnyDispose);
            if (dispose_state == __generator_internal_BeNotInitiatedAnyDispose)
            {

                // Dispose managed members and release unmaneged resources.
                Dispose(disposing: true);

                global::System.Threading.Thread.VolatileWrite(ref __generator_internal_disposeState, __generator_internal_DisposeAlreadyCompleted);
            }
        }
    }
}

#pragma warning disable CS0436
#nullable enable

namespace Benutomo
{
    /// <summary>
    /// <see cref=""Benutomo.AutomaticDisposeImplAttribute""/>を利用しているクラスで、ユーザが実装するマネージドオブジェクトを非同期的な処理による破棄を行うメソッドに付与する。このメソッドはデストラクタからは呼び出されない。デストラクタからも呼び出される必要がある場合はデストラクタで必要な処理を全て同期的に行うようにした上で<see cref=""Benutomo.UnmanagedResourceReleaseMethodAttribute"">を使用すること。この属性を付与するメソッドは引数なしで戻り値は<see cref=""System.Threading.ValueTask"" />などawait可能な型である必要がある。このメソッドはこのオブジェクトのDisposeAsync()が初めて実行された時に自動実装コードから呼び出される。ただし、このメソッドを所有するクラスがIDisposableも実装していて、かつ、Dispose()によってこのオブジェクトが破棄された場合は、この属性が付与されているメソッドは呼び出されず、<see cref=""Benutomo.ManagedObjectDisposeMethodAttribute"">が付与されているメソッドが呼び出される。
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Method)]
    internal class ManagedObjectAsyncDisposeMethodAttribute : global::System.Attribute
    {
        /// <summary>
        /// <inheritdoc cref=""Benutomo.ManagedObjectAsyncDisposeMethodAttribute""/>
        /// </summary>
        public ManagedObjectAsyncDisposeMethodAttribute() { }
    }
}
#pragma warning disable CS0436
#nullable enable

namespace Benutomo
{
    /// <summary>
    /// <see cref=""Benutomo.AutomaticDisposeImplAttribute""/>を利用しているクラスで、ユーザが実装するマネージドオブジェクトを同期的な処理による破棄を行うメソッドに付与する。このメソッドはデストラクタからは呼び出されない。デストラクタからも呼び出される必要がある場合は<see cref=""Benutomo.UnmanagedResourceReleaseMethodAttribute"">を使用すること。この属性を付与するメソッドは引数なしで戻り値はvoidである必要がある。このメソッドはこのオブジェクトのDispose()が初めて実行された時に自動実装コードから呼び出される。ただし、このメソッドを所有するクラスがIAsyncDisposableも実装していて、かつ、DisposeAsync()によってこのオブジェクトが破棄された場合は、この属性が付与されているメソッドは呼び出されず、<see cref=""Benutomo.ManagedObjectAsyncDisposeMethodAttribute"">が付与されているメソッドが呼び出される。
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Method)]
    internal class ManagedObjectDisposeMethodAttribute : global::System.Attribute
    {
        /// <summary>
        /// <inheritdoc cref=""Benutomo.ManagedObjectDisposeMethodAttribute""/>
        /// </summary>
        public ManagedObjectDisposeMethodAttribute() { }
    }
}
#pragma warning disable CS0436
#nullable enable

namespace Benutomo
{
    /// <summary>
    /// <see cref=""Benutomo.AutomaticDisposeImplAttribute""/>を利用しているクラスで、ユーザが実装するアンマネージドリソースの解放を行うメソッド(引数なしで戻り値はvoid)に付与する。このメソッドはこのオブジェクトのDispose()またはDisposeAsync()、デストラクタのいずれかが初めて実行された時に自動実装コードから呼び出される。この属性を付与したメソッドは、実装者の責任でGCのファイナライズスレッドから呼び出されても問題無いように実装しなければならないことに注意すること。
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Method)]
    internal class UnmanagedResourceReleaseMethodAttribute : global::System.Attribute
    {
        /// <summary>
        /// <inheritdoc cref=""Benutomo.UnmanagedResourceReleaseMethodAttribute""/>
        /// </summary>
        public UnmanagedResourceReleaseMethodAttribute() { }
    }
}
// <auto-generated/>
#pragma warning disable
#nullable enable
namespace IDisposableGeneratorDemo
{
    partial class DALDB : global::System.IDisposable
    {
#nullable disable
        /// <summary>
        /// Finalizes an instance of the <see cref = "Disposable"/> class.
        /// </summary>
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        ~DALDB()
        {
            Dispose(false);
        }

        /// <summary>
        /// Gets a value indicating whether this object is in the process of disposing.
        /// </summary>
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        public bool IsDisposing => global::System.Threading.Interlocked.CompareExchange(ref disposeStage, DisposalStarted, DisposalStarted) == DisposalStarted;

        /// <summary>
        /// Gets a value indicating whether this object has been disposed.
        /// </summary>
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        public bool IsDisposed => global::System.Threading.Interlocked.CompareExchange(ref disposeStage, DisposalComplete, DisposalComplete) == DisposalComplete;

        /// <summary>
        /// Gets a value indicating whether this object has been disposed or is in the process of being disposed.
        /// </summary>
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        public bool IsDisposedOrDisposing => global::System.Threading.Interlocked.CompareExchange(ref disposeStage, DisposalNotStarted, DisposalNotStarted) != DisposalNotStarted;

        /// <summary>
        /// Gets the object name, for use in any <see cref = "global::System.ObjectDisposedException"/> thrown by this object.
        /// </summary>
        /// <remarks>
        /// Subclasses can override this property if they would like more control over the object name appearing in any <see cref = "global::System.ObjectDisposedException"/>
        /// thrown by this <see cref = "Disposable"/>. This can be particularly useful in debugging and diagnostic scenarios.
        /// </remarks>
        /// <value>
        /// The object name, which defaults to the class name.
        /// </value>
        
#nullable enable
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected virtual string? ObjectName => GetType().FullName;

#nullable disable
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        private const int DisposalNotStarted = 0;
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        private const int DisposalStarted = 1;
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        private const int DisposalComplete = 2;
        // see the constants defined above for valid values
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        private int disposeStage;
        /// <summary>
        /// Occurs when this object is about to be disposed.
        /// </summary>
        
#nullable enable
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        public event global::System.EventHandler? Disposing;
        /// <summary>
        /// Disposes of this object, if it hasn't already been disposed.
        /// </summary>
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        public void Dispose()
        {
            if (global::System.Threading.Interlocked.CompareExchange(ref disposeStage, DisposalStarted, DisposalNotStarted) != DisposalNotStarted)
            {
                return;
            }

            OnDisposing();
            Disposing = null;
            Dispose(true);
            global::System.GC.SuppressFinalize(this);
            global::System.Threading.Interlocked.Exchange(ref disposeStage, DisposalComplete);
        }

        /// <summary>
        /// Verifies that this object is not in the process of disposing, throwing an exception if it is.
        /// </summary>
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected void VerifyNotDisposing()
        {
            if (IsDisposing)
            {
                throw new global::System.ObjectDisposedException(ObjectName);
            }
        }

        /// <summary>
        /// Verifies that this object has not been disposed, throwing an exception if it is.
        /// </summary>
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected void VerifyNotDisposed()
        {
            if (IsDisposed)
            {
                throw new global::System.ObjectDisposedException(ObjectName);
            }
        }

        /// <summary>
        /// Verifies that this object is not being disposed or has been disposed, throwing an exception if either of these are true.
        /// </summary>
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected void VerifyNotDisposedOrDisposing()
        {
            if (IsDisposedOrDisposing)
            {
                throw new global::System.ObjectDisposedException(ObjectName);
            }
        }

        /// <summary>
        /// Raises the <see cref = "Disposing"/> event.
        /// </summary>
        [global::System.CodeDom.Compiler.GeneratedCode("DisposableHelpers.SourceGenerators.DisposableGenerator", "1.0.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected virtual void OnDisposing()
        {
            Disposing?.Invoke(this, new global::System.EventArgs());
        }
    }
}
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by the Disposer source generator
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace IDisposableGeneratorDemo
{
    partial class DALDB : global::System.IDisposable
    {
        partial void DisposeManaged();
        partial void DisposeUnmanaged();

        private bool disposed = false;

        ~DALDB()
        {
            Dispose(false);
        }

        private void Dispose(bool disposing)
        {
            if (disposed)
                return;

            if (disposing)
            {
                DisposeManaged();
            }

            DisposeUnmanaged();

            disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            global::System.GC.SuppressFinalize(this);
        }
    }
}
// <autogenerated/>
namespace IDisposableGeneratorDemo;

// <autogenerated/>
#pragma warning disable SA1636, 8618
namespace IDisposableGenerator
{
    using System;

    // used only by a source generator to generate Dispose() and Dispose(bool).
    [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
    internal class CallOnDisposeAttribute : Attribute
    {
        public CallOnDisposeAttribute()
        {
        }
    }

    // used only by a source generator to generate Dispose() and Dispose(bool).
    [AttributeUsage(AttributeTargets.Event | AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
    internal class DisposeFieldAttribute : Attribute
    {
        public DisposeFieldAttribute(bool owner)
        {
        }
    }

    // used only by a source generator to generate Dispose() and Dispose(bool).
    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
    internal class GenerateDisposeAttribute : Attribute
    {
        public GenerateDisposeAttribute(bool stream)
        {
        }
    }

    // used only by a source generator to generate Dispose() and Dispose(bool).
    [AttributeUsage(AttributeTargets.Event | AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
    internal class NullOnDisposeAttribute : Attribute
    {
        public NullOnDisposeAttribute()
        {
        }
    }
}
#pragma warning restore SA1636, 8618

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/DisposableHelpers

RSCG – MagicMap

RSCG – MagicMap
 
 

name MagicMap
nuget https://www.nuget.org/packages/MagicMap/
link https://github.com/bramerdaniel/MagicMap
author Bramer Daniel

Auto mapping

 

This is how you can use MagicMap .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

	<ItemGroup>
	  <PackageReference Include="MagicMap" Version="1.0.0-alpha.10" OutputItemType="Analyzer" />
	</ItemGroup>


</Project>


The code that you will use is


// See https://aka.ms/new-console-template for more information
using mapperlyDemo;
var p=new Person();
p.FirstName = "Andrei";
p.LastName = "Ignat";
PersonDTO dto = p.ToPersonDTO();
Console.WriteLine(dto.FullName);




public class Person
{
    public int ID { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
}




using MagicMap;
namespace mapperlyDemo;

[TypeMapper(typeof(Person), typeof(PersonDTO))]
internal partial class PersonMapper { }
public class PersonDTO
{
    public int ID { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }

    public string FullName { 
        get
        {
            return FirstName + " " + LastName;
        }
    }
}


 

The code that is generated is

//------------------------------------------------
// <auto-generated>
//     Generated by the MagicMap source generator
// </auto-generated>
//------------------------------------------------
namespace mapperlyDemo
{
    [global::System.Runtime.CompilerServices.CompilerGenerated]
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute]
    partial class PersonMapper
    {
        /// <summary>
        /// The default singleton instance of the generated type mapper.
        /// To customize the creation of the default mapper, just implement this property in the defining partial part.
        /// </summary>
        public static PersonMapper Default { get; } = new PersonMapper(); 


        /// <summary>Maps all properties of the <see cref = "source"/> to the properties of the <see cref = "target"/></summary>
        public void Map(global::Person source, global::mapperlyDemo.PersonDTO target)
        {
            target.ID = source.ID;
            target.FirstName = source.FirstName;
            target.LastName = source.LastName;
            MapOverride(source, target);
        }

        internal global::mapperlyDemo.PersonDTO MapFrom(global::Person source)
        {
            var target = Default is MagicMap.ITypeFactory<global::mapperlyDemo.PersonDTO, global::Person> factory ? factory.Create(source) : new global::mapperlyDemo.PersonDTO();
            Default.Map(source, target);
            return target;
        }

        /// <summary>Maps all properties of the <see cref = "source"/> to the properties of the <see cref = "target"/></summary>
        public void Map(global::mapperlyDemo.PersonDTO source, global::Person target)
        {
            target.ID = source.ID;
            target.FirstName = source.FirstName;
            target.LastName = source.LastName;
            MapOverride(source, target);
        }

        internal global::Person MapFrom(global::mapperlyDemo.PersonDTO source)
        {
            var target = Default is MagicMap.ITypeFactory<global::Person, global::mapperlyDemo.PersonDTO> factory ? factory.Create(source) : new global::Person();
            Default.Map(source, target);
            return target;
        }

        /// <summary>Implement this method, to map the properties the mapper could not handle for any reason.</summary>
        partial void MapOverride(global::Person source, global::mapperlyDemo.PersonDTO target); 

        /// <summary>Implement this method, to map the properties the mapper could not handle for any reason.</summary>
        partial void MapOverride(global::mapperlyDemo.PersonDTO source, global::Person target); 

    }
}

//------------------------------------------------
// <auto-generated>
//     Generated by the MagicMap source generator
// </auto-generated>
//------------------------------------------------
namespace mapperlyDemo
{
    [global::System.Runtime.CompilerServices.CompilerGenerated]
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute]
    internal static partial class PersonMapperExtensions
    {
        /// <summary>
        /// The instance of the <see cref = "PersonMapper"/> all extension methods use.
        /// You can customize this by implementing this property in your own partial implementation of the extensions class.
        /// </summary>
        private static global::mapperlyDemo.PersonMapper Mapper => global::mapperlyDemo.PersonMapper.Default; 


        internal static global::mapperlyDemo.PersonDTO ToPersonDTO(this global::Person person)
        {
            if (person == null)
                throw new global::System.ArgumentNullException(nameof(person)); 

            var result = Mapper is MagicMap.ITypeFactory<global::mapperlyDemo.PersonDTO, global::Person> factory ? factory.Create(person) : new global::mapperlyDemo.PersonDTO();
            Mapper.Map(person, result);
            return result;
        }

        internal static global::Person ToPerson(this global::mapperlyDemo.PersonDTO personDTO)
        {
            if (personDTO == null)
                throw new global::System.ArgumentNullException(nameof(personDTO)); 

            var result = Mapper is MagicMap.ITypeFactory<global::Person, global::mapperlyDemo.PersonDTO> factory ? factory.Create(personDTO) : new global::Person();
            Mapper.Map(personDTO, result);
            return result;
        }
    }
}

//------------------------------------------------
// <auto-generated>
//     Generated by the MagicMap source generator
// </auto-generated>
//------------------------------------------------
namespace MagicMap 
{
   [global::System.Runtime.CompilerServices.CompilerGenerated]
   [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute]
   [global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
   /// <summary>Attribute that can be used mark a method as mapping method for a specific source property</summary>
   internal sealed class PropertyMapperAttribute : global::System.Attribute
   {
      /// <summary>Marks a function as a custom mapping method for the specified property.</summary>
      public PropertyMapperAttribute(global::System.Type targetType, string targetName)
         : this(targetType, targetName, targetName)
      {
      }

      /// <summary>Marks a function as a custom mapping method for the specified property.</summary>
      public PropertyMapperAttribute(global::System.Type targetType, string targetName, string sourceName)
      {
         TargetType = targetType ?? throw new global::System.ArgumentNullException(nameof(targetType));
         TargetName = targetName ?? throw new global::System.ArgumentNullException(nameof(targetName));
         SourceName  = sourceName ?? throw new global::System.ArgumentNullException(nameof(sourceName));
      }

      /// <summary>Gets the type of the target class, the mapper should be used for.</summary>
      public global::System.Type TargetType { get; }

      /// <summary>Gets the name of the property that should be mapped to.</summary>
      public string TargetName { get; }

      /// <summary>Gets the name of the source property that should be mapped from.</summary>
      public string SourceName { get; }
   }
}

//------------------------------------------------
// <auto-generated>
//     Generated by the MagicMap source generator
// </auto-generated>
//------------------------------------------------
namespace MagicMap 
{
   [global::System.Runtime.CompilerServices.CompilerGenerated]
   [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute]
   [global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
   /// <summary>Attribute that can be used to map a specific property from the left to the right object by their names</summary>
   internal sealed class PropertyMappingAttribute : global::System.Attribute
   {
      public PropertyMappingAttribute(string leftName, string rightName)
      {
         LeftName = leftName ?? throw new global::System.ArgumentNullException(nameof(leftName));
         RightName = rightName ?? throw new global::System.ArgumentNullException(nameof(rightName));
      }

      /// <summary>Gets the name of the left property.</summary>
      public string LeftName { get; }

      /// <summary>Gets the name of the right property.</summary>
      public string RightName { get; }

      /// <summary>Gets or sets a value indicating that the properties should be ignored for mapping generation.</summary>
      public bool Ignore{ get; set; }
   }
}

//------------------------------------------------
// <auto-generated>
//     Generated by the MagicMap source generator
// </auto-generated>
//------------------------------------------------
namespace MagicMap 
{
   [global::System.Runtime.CompilerServices.CompilerGenerated]
   internal interface ITypeFactory<TTarget, TSource>  where TTarget : class
   {
      /// <summary>Created an instance of the type TTarget, that is used for a mapping.</summary>
      TTarget Create(TSource source);
   }
}

//------------------------------------------------
// <auto-generated>
//     Generated by the MagicMap source generator
// </auto-generated>
//------------------------------------------------
namespace MagicMap 
{
   [global::System.Runtime.CompilerServices.CompilerGenerated]
   [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute]
   [global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
   internal sealed class TypeMapperAttribute : global::System.Attribute
   {
      public TypeMapperAttribute(global::System.Type left, global::System.Type right)
      {
         Left = left ?? throw new global::System.ArgumentNullException(nameof(left));
         Right = right ?? throw new global::System.ArgumentNullException(nameof(right));
      }

      /// <summary>Gets the left type.</summary>
      public global::System.Type Left { get; }

      /// <summary>Gets the right type.</summary>
      public global::System.Type Right { get; }

      /// <summary>Gets or sets the <see cref="GenerationMode"/>.</summary>
      public GeneratorMode Mode { get; set; } = GeneratorMode.TwoWay;

      /// <summary>Gets or sets a flag, indicating if the source generator should generate partial methods
      /// for properties he can not map, to enforce that they are handled by the user.</summary>
      public bool ForceMappings { get; set; } = false;
   }

   /// <summary>Enum for configuring the source generator mode</summary>
   internal enum GeneratorMode
   {
      /// <summary>Mappers are generated for both ways, from left to right and back</summary>
      TwoWay,

      /// <summary>Only a mapper from left to right typ is generated</summary>
      LeftToRight,

      /// <summary>Only a mapper from right to left typ is generated</summary>
      RightToLeft
   }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/MagicMap

RSCG – RSCG_Templating

RSCG – RSCG_Templating
 
 

name RSCG_Templating
nuget https://www.nuget.org/packages/RSCG_Templating/
https://www.nuget.org/packages/RSCG_TemplatingCommon
link https://github.com/ignatandrei/rscg_templating/
author Andrei Ignat

Templating every your data ( starting with class)

 

This is how you can use RSCG_Templating .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
	<ItemGroup>
		<AdditionalFiles Include="ClassTypeName.txt" />
		<AdditionalFiles Include="ClassPropByName.txt" />
	</ItemGroup>



	<ItemGroup>
    <PackageReference Include="RSCG_Templating" Version="2023.1007.724" OutputItemType="Analyzer"  ReferenceOutputAssembly="false"   />
    <PackageReference Include="RSCG_TemplatingCommon" Version="2023.1007.724" />
  </ItemGroup>


	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

</Project>


The code that you will use is


using RSCG_TemplatingDemo;

var x = new Person();
Console.WriteLine("The generated string type is " + x.MyTypeName);
x.FirstName = "Andrei";
//set last name via prop
x.SetPropValue(ePerson_Properties.LastName, "Ignat");
Console.WriteLine("called directly first name : " + x.FirstName);
Console.WriteLine("called via enum of prop first name : " + x.GetPropValue(ePerson_Properties.FirstName));
Console.WriteLine("called get property :" + x.GetPropValue(ePerson_Properties.Name));

Console.WriteLine("this will throw error because Name has not set ");
try
{
    x.SetPropValue(ePerson_Properties.Name, "asd");
}
catch (Exception)
{
    Console.WriteLine("this is good!");
}
Console.ReadLine();


using RSCG_TemplatingCommon;

namespace RSCG_TemplatingDemo;

[IGenerateDataFromClass("ClassTypeName")]
[IGenerateDataFromClass("ClassPropByName")]
public partial class Person
{
    public string Name { get { return FullName(" "); } }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public string FullName(string separator = " ")
    {
        return FirstName + separator + LastName;
    }
    public void DisplayNameOnConsole()
    {
        Console.WriteLine(FullName());
    }
    public async Task<string> GetName()
    {
        await Task.Delay(1000);
        return FirstName ?? "";
    }
    public Task<string> GetFullName()
    {
        return Task.FromResult(FullName());
    }
    public Task SaveId(int id)
    {
        if (id < 0)
        {
            throw new ArgumentException("this is an error because is <0 ");
        }
        return Task.CompletedTask;
    }
}


 

The code that is generated is

//autogenerated by RSCG_Templating version 2023.1007.724.0 from file Microsoft.CodeAnalysis.AdditionalTextFile
namespace RSCG_TemplatingDemo {
	public enum ePerson_Properties {
		None = 0,
		
				Name,
				
				FirstName,
				
				LastName,
				
	} 
	partial class Person {

		public object GetPropValue(ePerson_Properties prop){
			switch(prop){
				
									case ePerson_Properties.Name:
									
											return this.Name;
										
								
									case ePerson_Properties.FirstName:
									
											return this.FirstName;
										
								
									case ePerson_Properties.LastName:
									
											return this.LastName;
										
								
				default:
						throw new NotImplementedException();
			}
		}
		public void SetPropValue<T>(ePerson_Properties prop , T value){
			switch(prop){
				
									case ePerson_Properties.Name:
									
											throw new NotImplementedException();
										
								
									case ePerson_Properties.FirstName:
									
											this.FirstName = (string?)(dynamic)value;
											break;
										
								
									case ePerson_Properties.LastName:
									
											this.LastName = (string?)(dynamic)value;
											break;
										
								
				default:
						throw new NotImplementedException();
			}
		}
	}//end class

}//end namespace
//autogenerated by RSCG_Templating version 2023.1007.724.0 from file Microsoft.CodeAnalysis.AdditionalTextFile
namespace RSCG_TemplatingDemo {
	 
	partial class Person {
		public string MyTypeName = "RSCG_TemplatingDemo.Person";		
	}//end class

}//end namespace

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/RSCG_Templating

RSCG – MapTo

RSCG – MapTo
 
 

name MapTo
nuget https://www.nuget.org/packages/MapTo/
link https://github.com/mrtaikandi/MapTo
author Mohammedreza Taikandi

AutoGenerate Mapping

 

This is how you can use MapTo .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

	<ItemGroup>
	  <PackageReference Include="MapTo" Version="0.9.1" />
	</ItemGroup>

</Project>


The code that you will use is


// See https://aka.ms/new-console-template for more information
using mapperlyDemo;
var p=new Person();
p.FirstName = "Andrei";
p.LastName = "Ignat";
PersonDTO dto = p.MapToPersonDTO();
Console.WriteLine(dto.FullName);




public class Person
{
    public int ID { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
}




using MapTo;

namespace mapperlyDemo;
[MapFrom(typeof(Person))]
public class PersonDTO
{
    public int ID { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }

    [IgnoreProperty]
    public string FullName { 
        get
        {
            return FirstName + " " + LastName;
        }
    }
}


 

The code that is generated is

// <auto-generated />
#nullable enable

namespace mapperlyDemo;

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("MapTo", "0.9.1.51471")]
public static class PersonMapToExtensions
{
    [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("person")]
    public static global::mapperlyDemo.PersonDTO? MapToPersonDTO(this Person? person)
    {
        if (ReferenceEquals(person, null))
        {
            return null;
        }

        return new PersonDTO
        {
            ID = person.ID,
            FirstName = person.FirstName,
            LastName = person.LastName
        };
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/MapTo

RSCG – MapTo

RSCG – MapTo
 
 

name MapTo
nuget https://www.nuget.org/packages/MapTo/
link https://github.com/mrtaikandi/MapTo
author Mohammedreza Taikandi

AutoGenerate Mapping

 

This is how you can use MapTo .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

	<ItemGroup>
	  <PackageReference Include="MapTo" Version="0.9.1" />
	</ItemGroup>

</Project>


The code that you will use is


// See https://aka.ms/new-console-template for more information
using mapperlyDemo;
var p=new Person();
p.FirstName = "Andrei";
p.LastName = "Ignat";
PersonDTO dto = p.MapToPersonDTO();
Console.WriteLine(dto.FullName);




public class Person
{
    public int ID { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
}




using MapTo;

namespace mapperlyDemo;
[MapFrom(typeof(Person))]
public class PersonDTO
{
    public int ID { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }

    [IgnoreProperty]
    public string FullName { 
        get
        {
            return FirstName + " " + LastName;
        }
    }
}


 

The code that is generated is

// <auto-generated />
#nullable enable

namespace mapperlyDemo;

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("MapTo", "0.9.1.51471")]
public static class PersonMapToExtensions
{
    [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("person")]
    public static global::mapperlyDemo.PersonDTO? MapToPersonDTO(this Person? person)
    {
        if (ReferenceEquals(person, null))
        {
            return null;
        }

        return new PersonDTO
        {
            ID = person.ID,
            FirstName = person.FirstName,
            LastName = person.LastName
        };
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/MapTo

RSCG – JsonPolymorphicGenerator

RSCG – JsonPolymorphicGenerator
 
 

name JsonPolymorphicGenerator
nuget https://www.nuget.org/packages/GoLive.Generator.JsonPolymorphicGenerator/
link https://github.com/surgicalcoder/JsonPolymorphicGenerator
author surgicalcoder

Generating JsonDerivedType to be added to the base class

 

This is how you can use JsonPolymorphicGenerator .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="GoLive.Generator.JsonPolymorphicGenerator" Version="1.0.4">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

</Project>


The code that you will use is


//https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism?pivots=dotnet-7-0
using JsonPolymorphicGeneratorDemo;
using System.Text.Json;

Person[] persons = new Person[2];
persons[0] = new Student() { Name="Student Ignat"};

persons[1] = new Teacher() { Name = "Teacher Ignat" };
JsonSerializerOptions opt = new ()
{
    WriteIndented = true
};
var ser = JsonSerializer.Serialize(persons, opt);
Console.WriteLine(ser);
var p = JsonSerializer.Deserialize<Person[]>(ser);
if(p != null)
foreach (var item in p)
{
    Console.WriteLine(item.Data());
}


using System.Text.Json.Serialization;

namespace JsonPolymorphicGeneratorDemo;

[JsonPolymorphic]
public abstract partial class Person
{
    
    public string? Name { get; set; }
    public abstract string Data();
}

public class Teacher : Person
{
    public override string Data()
    {
        return "Class Teacher:" + Name;
    }
}
public class Student : Person
{
    public override string Data()
    {
        return "Class Student:" + Name;
    }
}


 

The code that is generated is

using System.Text.Json.Serialization;

namespace JsonPolymorphicGeneratorDemo
{
    [JsonDerivedType(typeof(JsonPolymorphicGeneratorDemo.Teacher), "Teacher")]
    [JsonDerivedType(typeof(JsonPolymorphicGeneratorDemo.Student), "Student")]
    public partial class Person
    {
    }
}



Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/JsonPolymorphicGenerator

RSCG – BuilderGenerator

RSCG – BuilderGenerator
 
 

name BuilderGenerator
nuget https://www.nuget.org/packages/BuilderGenerator/
link https://github.com/MelGrubb/BuilderGenerator
author Mell Grubb

Generating Builder class for an object

 

This is how you can use BuilderGenerator .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
  </PropertyGroup>

	  <PropertyGroup>
        <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
        <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
    </PropertyGroup>

	  <ItemGroup>
	    <PackageReference Include="BuilderGenerator" Version="2.3.0" />
	  </ItemGroup>

</Project>


The code that you will use is


using Builder;

var pOld = new Person();
pOld.FirstName = "Andrei";
pOld.LastName = "Ignat";
pOld.MiddleName = "G";
var build = new PersonBuilder()
    .WithFirstName(pOld.FirstName)
    .WithMiddleName("")
    .WithLastName(pOld.LastName)
    ;
    
var pNew = build.Build();
System.Console.WriteLine(pNew.FullName());
System.Console.WriteLine(pOld.FullName());



namespace Builder;
public class Person
{
    public string FirstName { get; set; }
    public string? MiddleName { get; set; }
    public string LastName { get; set; }

    public string FullName()
    {
        return FirstName + " " + MiddleName + " "+LastName;
    }
    
}



namespace Builder;

[BuilderGenerator.BuilderFor(typeof(Person))]
public partial class PersonBuilder
{
}

 

The code that is generated is

#nullable disable

namespace BuilderGenerator
{
    /// <summary>Base class for object builder classes.</summary>
    /// <typeparam name="T">The type of the objects built by this builder.</typeparam>
    public abstract class Builder<T> where T : class
    {
        /// <summary>Gets or sets the object returned by this builder.</summary>
        /// <value>The constructed object.</value>
        #pragma warning disable CA1720 // Identifier contains type name
        protected System.Lazy<T> Object { get; set; }
        #pragma warning restore CA1720 // Identifier contains type name

        /// <summary>Builds the object instance.</summary>
        /// <returns>The constructed object.</returns>
        public abstract T Build();

        protected virtual void PostProcess(T value)
        {
        }

        /// <summary>Sets the object to be returned by this instance.</summary>
        /// <param name="value">The object to be returned.</param>
        /// <returns>A reference to this builder instance.</returns>
        public Builder<T> WithObject(T value)
        {
            Object = new System.Lazy<T>(() => value);

            return this;
        }
    }
}

namespace BuilderGenerator
{
    [System.AttributeUsage(System.AttributeTargets.Class)]
    public class BuilderForAttribute : System.Attribute
    {
        public bool IncludeInternals { get; }
        public System.Type Type { get; }

        public BuilderForAttribute(System.Type type, bool includeInternals = false)
        {
            IncludeInternals = includeInternals;
            Type = type;
        }
    }
}

#nullable disable

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by BuilderGenerator at 2023-10-02T22:28:35 in 10.3936ms.
// </auto-generated>
//------------------------------------------------------------------------------
using System.CodeDom.Compiler;


namespace Builder
{
    public partial class PersonBuilder : BuilderGenerator.Builder<Builder.Person>
    {
        public System.Lazy<string> FirstName = new System.Lazy<string>(() => default(string));
        public System.Lazy<string> LastName = new System.Lazy<string>(() => default(string));
        public System.Lazy<string?> MiddleName = new System.Lazy<string?>(() => default(string?));

        public override Builder.Person Build()
        {
            if (Object?.IsValueCreated != true)
            {
                Object = new System.Lazy<Builder.Person>(() =>
                {
                    var result = new Builder.Person
                    {
                        FirstName = FirstName.Value,
                        LastName = LastName.Value,
                        MiddleName = MiddleName.Value,
                    };

                    return result;
                });

                PostProcess(Object.Value);
            }

            return Object.Value;
        }

        public PersonBuilder WithFirstName(string value)
        {
            return WithFirstName(() => value);
        }

        public PersonBuilder WithFirstName(System.Func<string> func)
        {
            FirstName = new System.Lazy<string>(func);
            return this;
        }

        public PersonBuilder WithoutFirstName()
        {
            FirstName = new System.Lazy<string>(() => default(string));
            return this;
        }

        public PersonBuilder WithLastName(string value)
        {
            return WithLastName(() => value);
        }

        public PersonBuilder WithLastName(System.Func<string> func)
        {
            LastName = new System.Lazy<string>(func);
            return this;
        }

        public PersonBuilder WithoutLastName()
        {
            LastName = new System.Lazy<string>(() => default(string));
            return this;
        }

        public PersonBuilder WithMiddleName(string? value)
        {
            return WithMiddleName(() => value);
        }

        public PersonBuilder WithMiddleName(System.Func<string?> func)
        {
            MiddleName = new System.Lazy<string?>(func);
            return this;
        }

        public PersonBuilder WithoutMiddleName()
        {
            MiddleName = new System.Lazy<string?>(() => default(string?));
            return this;
        }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/BuilderGenerator

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.