Category: roslyn

RSCG – ActorSrcGen

 
 

name ActorSrcGen
nuget https://www.nuget.org/packages/ActorSrcGen/
link https://github.com/aabs/ActorSrcGen
author Andrew Matthews

Generating source code for actors in C#.

 

This is how you can use ActorSrcGen .

The code that you start with is


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

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

  <ItemGroup>
    <PackageReference Include="ActorSrcGen" Version="1.1.2" />
    <PackageReference Include="ActorSrcGen.Abstractions" Version="1.1.2" />
    <PackageReference Include="Gridsum.DataflowEx" Version="2.0.0" />
  </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
using ActorDemo;
using Gridsum.DataflowEx;

Person person = new Person { Name = "Andrei Ignat" };

DayWorkflow dayAndreiIgnat = new ();
var input = dayAndreiIgnat.InputBlock;
//async
await dayAndreiIgnat.SendAsync(person);
//sync
while (dayAndreiIgnat.Call(person))
{
    await Task.Delay(100);
}

Console.WriteLine("Done");
Console.ReadLine();


using ActorSrcGen;
using System.Diagnostics.Metrics;

namespace ActorDemo;
[Actor]
partial class DayWorkflow
{
    [FirstStep("StartDay")]
    //[Receiver]
    [NextStep(nameof(WashFace))]
    [NextStep(nameof(LogMessage))]
    public async Task<Person> StartDay(Person p)
    {
        await Task.Delay(1000 );
        return p;
    }

    

    [Step]
    [NextStep(nameof(LogMessage))]
    [NextStep(nameof(Eat))]
    public async Task<Person> WashFace(Person p)
    {
        await Task.Delay(1000);
        return p;
    }
    


    [Step]
    [NextStep(nameof(LogMessage))]
    [NextStep(nameof(Sleep))]
    public async Task<Person> Eat(Person p)
    {
        await Task.Delay(1000);
        return p;
    }
    

    [NextStep(nameof(LogMessage))]
    public async Task<int> Sleep(Person p)
    {
        await Task.Delay(1000);
        return p.Name.Length;
    }

    [LastStep]
    public void LogMessage(Person x)
    {
        Console.WriteLine("Incoming Message: " + x?.Name);
    }


}




namespace ActorDemo;
public class Person
{
    public string Name { get; set; }
}


 

The code that is generated is

// Generated on 2024-06-02
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS0108 // hides inherited member.

using ActorSrcGen;
using System.Diagnostics.Metrics;
namespace ActorDemo;
using System.Threading.Tasks.Dataflow;
using Gridsum.DataflowEx;
public partial class DayWorkflow : Dataflow<Person>, IActor<Person>
{
    public DayWorkflow() : base(DataflowOptions.Default)
    {
        _LogMessage = new ActionBlock<Person>(        (Person x) => {
            try
            {
                LogMessage(x);
            }catch{}
        },
            new ExecutionDataflowBlockOptions() {
                BoundedCapacity = 5,
                MaxDegreeOfParallelism = 8
        });
        RegisterChild(_LogMessage);
        _Eat = new TransformManyBlock<Person,Person>(       async (Person x) => {
           var result = new List<Person>();
           try
           {
               var newValue = await Eat(x);
               result.Add(newValue);
           }catch{}
           return result;
       },
            new ExecutionDataflowBlockOptions() {
                BoundedCapacity = 5,
                MaxDegreeOfParallelism = 8
        });
        RegisterChild(_Eat);
        _EatBC = new BroadcastBlock<Person>(    (Person x) => x,
            new ExecutionDataflowBlockOptions() {
                BoundedCapacity = 5,
                MaxDegreeOfParallelism = 8
        });
        RegisterChild(_EatBC);
        _WashFace = new TransformManyBlock<Person,Person>(       async (Person x) => {
           var result = new List<Person>();
           try
           {
               var newValue = await WashFace(x);
               result.Add(newValue);
           }catch{}
           return result;
       },
            new ExecutionDataflowBlockOptions() {
                BoundedCapacity = 5,
                MaxDegreeOfParallelism = 8
        });
        RegisterChild(_WashFace);
        _WashFaceBC = new BroadcastBlock<Person>(    (Person x) => x,
            new ExecutionDataflowBlockOptions() {
                BoundedCapacity = 5,
                MaxDegreeOfParallelism = 8
        });
        RegisterChild(_WashFaceBC);
        _StartDay = new TransformManyBlock<Person,Person>(       async (Person x) => {
           var result = new List<Person>();
           try
           {
               var newValue = await StartDay(x);
               result.Add(newValue);
           }catch{}
           return result;
       },
            new ExecutionDataflowBlockOptions() {
                BoundedCapacity = 5,
                MaxDegreeOfParallelism = 8
        });
        RegisterChild(_StartDay);
        _StartDayBC = new BroadcastBlock<Person>(    (Person x) => x,
            new ExecutionDataflowBlockOptions() {
                BoundedCapacity = 5,
                MaxDegreeOfParallelism = 8
        });
        RegisterChild(_StartDayBC);
        _Eat.LinkTo(_EatBC, new DataflowLinkOptions { PropagateCompletion = true });
        _EatBC.LinkTo(_LogMessage, new DataflowLinkOptions { PropagateCompletion = true });
        _WashFace.LinkTo(_WashFaceBC, new DataflowLinkOptions { PropagateCompletion = true });
        _WashFaceBC.LinkTo(_LogMessage, new DataflowLinkOptions { PropagateCompletion = true });
        _WashFaceBC.LinkTo(_Eat, new DataflowLinkOptions { PropagateCompletion = true });
        _StartDay.LinkTo(_StartDayBC, new DataflowLinkOptions { PropagateCompletion = true });
        _StartDayBC.LinkTo(_LogMessage, new DataflowLinkOptions { PropagateCompletion = true });
        _StartDayBC.LinkTo(_WashFace, new DataflowLinkOptions { PropagateCompletion = true });
    }

    ActionBlock<Person> _LogMessage;

    TransformManyBlock<Person,Person> _Eat;

    BroadcastBlock<Person> _EatBC;

    TransformManyBlock<Person,Person> _WashFace;

    BroadcastBlock<Person> _WashFaceBC;

    TransformManyBlock<Person,Person> _StartDay;

    BroadcastBlock<Person> _StartDayBC;
    public override ITargetBlock<Person> InputBlock { get => _StartDay; }
    public bool Call(Person input)
        => InputBlock.Post(input);

    public async Task<bool> Cast(Person input)
        => await InputBlock.SendAsync(input);
}

Code and pdf at

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

RSCG – MSTest

name MSTest
nuget https://www.nuget.org/packages/MSTest.SourceGeneration/
link https://github.com/microsoft/testfx
author Microsoft

AOP for MSTest

 

This is how you can use MSTest .

The code that you start with is


<!-- file: UnitTestProject1.csproj -->
<Project Sdk="Microsoft.NET.Sdk">

	<PropertyGroup>
		<TargetFramework>net8.0</TargetFramework>
		<ImplicitUsings>enable</ImplicitUsings>
		<Nullable>enable</Nullable>

		<OutputType>exe</OutputType>
		<PublishAot>true</PublishAot>
	</PropertyGroup>

	<ItemGroup>
		<!-- 
      Experimental MSTest Engine & source generator, 
      close sourced, licensed the same as our extensions 
      with Microsoft Testing Platform Tools license.
    -->
		<PackageReference Include="MSTest.Engine" Version="1.0.0-alpha.24163.4" />
		<PackageReference Include="MSTest.SourceGeneration" Version="1.0.0-alpha.24163.4" />

		<PackageReference Include="Microsoft.CodeCoverage.MSBuild" Version="17.10.4" />
		<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" Version="17.10.4" />

		<PackageReference Include="Microsoft.Testing.Extensions.TrxReport" Version="1.0.2" />
		<PackageReference Include="Microsoft.Testing.Platform.MSBuild" Version="1.0.2" />
		<PackageReference Include="MSTest.TestFramework" Version="3.2.2" />
		<PackageReference Include="MSTest.Analyzers" Version="3.2.2" />

	</ItemGroup>

	<ItemGroup>
		<ProjectReference Include="..\MyImportantClass\MyImportantClass.csproj" />
	</ItemGroup>

	<ItemGroup>
		<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
	</ItemGroup>

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

</Project>

The code that you will use is


// file: UnitTest1.cs
using MyImportantClass;

namespace DemoTest;

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        Assert.AreEqual(3, new Class1().Add(1, 2));
    }

    [TestMethod]
    [DataRow(1, 2)]
    [DataRow(100, -97)]
    public void TestMethod2(int left, int right)
    {
        Assert.AreEqual(3, new Class1().Add(left, right));
    }
}

 

The code that is generated is

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by Microsoft Testing Framework Generator.
// </auto-generated>
//------------------------------------------------------------------------------

namespace DemoTest
{
    using Threading = global::System.Threading;
    using ColGen = global::System.Collections.Generic;
    using CA = global::System.Diagnostics.CodeAnalysis;
    using Sys = global::System;
    using Msg = global::Microsoft.Testing.Platform.Extensions.Messages;
    using MSTF = global::Microsoft.Testing.Framework;

    [CA::ExcludeFromCodeCoverage]
    public static class DemoTest_UnitTest1
    {
        public static readonly MSTF::TestNode TestNode = new MSTF::TestNode
        {
            StableUid = "DemoTest.DemoTest.UnitTest1",
            DisplayName = "UnitTest1",
            Properties = new Msg::IProperty[1]
            {
                new Msg::TestFileLocationProperty(@"D:\gth\RSCG_Examples\v2\rscg_examples\MSTest\src\DemoTest\UnitTest1.cs", new(new(6, -1), new(22, -1))),
            },
            Tests = new MSTF::TestNode[]
            {
                new MSTF::InternalUnsafeActionTestNode
                {
                    StableUid = "DemoTest.DemoTest.UnitTest1.TestMethod1()",
                    DisplayName = "TestMethod1",
                    Properties = new Msg::IProperty[2]
                    {
                        new Msg::TestMethodIdentifierProperty(
                            "DemoTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
                            "DemoTest",
                            "UnitTest1",
                            "TestMethod1",
                            Sys::Array.Empty<string>(),
                            "System.Void"),
                        new Msg::TestFileLocationProperty(@"D:\gth\RSCG_Examples\v2\rscg_examples\MSTest\src\DemoTest\UnitTest1.cs", new(new(9, -1), new(13, -1))),
                    },
                    Body = static testExecutionContext =>
                    {
                        var instance = new UnitTest1();
                        try
                        {
                            instance.TestMethod1();
                        }
                        catch (global::System.Exception ex)
                        {
                            testExecutionContext.ReportException(ex, null);
                        }
                    },
                },
                new MSTF::InternalUnsafeActionParameterizedTestNode<MSTF::InternalUnsafeTestArgumentsEntry<(int left, int right)>>
                {
                    StableUid = "DemoTest.DemoTest.UnitTest1.TestMethod2(int, int)",
                    DisplayName = "TestMethod2",
                    Properties = new Msg::IProperty[2]
                    {
                        new Msg::TestMethodIdentifierProperty(
                            "DemoTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
                            "DemoTest",
                            "UnitTest1",
                            "TestMethod2",
                            new string[2]
                            {
                                "System.Int32",
                                "System.Int32",
                            },
                            "System.Void"),
                        new Msg::TestFileLocationProperty(@"D:\gth\RSCG_Examples\v2\rscg_examples\MSTest\src\DemoTest\UnitTest1.cs", new(new(15, -1), new(21, -1))),
                    },
                    GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry<(int left, int right)>[]
                    {
                        new MSTF::InternalUnsafeTestArgumentsEntry<(int left, int right)>((1, 2), "left: 1, right: 2"),
                        new MSTF::InternalUnsafeTestArgumentsEntry<(int left, int right)>((100, -97), "left: 100, right: -97"),
                    },
                    Body = static (testExecutionContext, data) =>
                    {
                        var instance = new UnitTest1();
                        try
                        {
                            instance.TestMethod2(data.Arguments.left, data.Arguments.right);
                        }
                        catch (global::System.Exception ex)
                        {
                            testExecutionContext.ReportException(ex, null);
                        }
                    },
                },
            },
        };
    }
}


namespace Microsoft.Testing.Framework.SourceGeneration
{
    public static class SourceGeneratedTestingPlatformBuilderHook
    {
        public static void AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder testApplicationBuilder, string[] _)
        {
            testApplicationBuilder.AddTestFramework(new Microsoft.Testing.Framework.Configurations.TestFrameworkConfiguration(System.Environment.ProcessorCount),
                new DemoTest.SourceGeneratedTestNodesBuilder());
        }
    }
}
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by Microsoft Testing Framework Generator.
// </auto-generated>
//------------------------------------------------------------------------------

namespace DemoTest
{
    using DemoTest;
    using ColGen = global::System.Collections.Generic;
    using CA = global::System.Diagnostics.CodeAnalysis;
    using Sys = global::System;
    using Tasks = global::System.Threading.Tasks;
    using Msg = global::Microsoft.Testing.Platform.Extensions.Messages;
    using MSTF = global::Microsoft.Testing.Framework;
    using Cap = global::Microsoft.Testing.Platform.Capabilities.TestFramework;
    using TrxReport = global::Microsoft.Testing.Extensions.TrxReport.Abstractions;

    [CA::ExcludeFromCodeCoverage]
    public sealed class SourceGeneratedTestNodesBuilder : MSTF::ITestNodesBuilder
    {
        private sealed class ClassCapabilities : TrxReport::ITrxReportCapability
        {
            bool TrxReport::ITrxReportCapability.IsSupported { get; } = true;
            void TrxReport::ITrxReportCapability.Enable() {}
        }

        public ColGen::IReadOnlyCollection<Cap::ITestFrameworkCapability> Capabilities { get; } = new Cap::ITestFrameworkCapability[1] { new ClassCapabilities() };

        public Tasks::Task<MSTF::TestNode[]> BuildAsync(MSTF::ITestSessionContext testSessionContext)
        {
            ColGen::List<MSTF::TestNode> namespace1Tests = new();
            namespace1Tests.Add(DemoTest_UnitTest1.TestNode);

            MSTF::TestNode root = new MSTF::TestNode
            {
                StableUid = "DemoTest",
                DisplayName = "DemoTest",
                Properties = Sys::Array.Empty<Msg::IProperty>(),
                Tests = new MSTF::TestNode[]
                {
                    new MSTF::TestNode
                    {
                        StableUid = "DemoTest.DemoTest",
                        DisplayName = "DemoTest",
                        Properties = Sys::Array.Empty<Msg::IProperty>(),
                        Tests = namespace1Tests.ToArray(),
                    },
                },
            };

            return Tasks::Task.FromResult(new MSTF::TestNode[1] { root });
        }
    }
}

Code and pdf at

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

RSCG – Minerals.AutoMixins

 
 

name Minerals.AutoMixins
nuget https://www.nuget.org/packages/Minerals.AutoMixins/
link https://github.com/SzymonHalucha/Minerals.AutoMixins
author Szymon Halucha

Generate Mixin from another classes

 

This is how you can use Minerals.AutoMixins .

The code that you start with is


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

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

  <ItemGroup>
    <PackageReference Include="Minerals.AutoMixins" Version="0.2.1" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using DemoMixin;

Person person = new Person { Name = "Andrei Ignat" };
person.LogName();



namespace DemoMixin;
[Minerals.AutoMixins.AddMixin(typeof(LogData))]
internal partial class Person
{
    public string Name { get; set; }
    public void LogName() => Log(Name);
}




namespace DemoMixin;
[Minerals.AutoMixins.GenerateMixin]
internal class LogData
{
    public void Log(string data) => Console.WriteLine(data);
}


 

The code that is generated is

// <auto-generated>
// This code was generated by a tool.
// Name: Minerals.AutoMixins
// Version: 0.2.1+6c5634e46b130effbe00bd9d3f94459f1dbb2e85
// </auto-generated>

namespace DemoMixin
{
    [global::System.Diagnostics.DebuggerNonUserCode]
    [global::System.Runtime.CompilerServices.CompilerGenerated]
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
    internal partial class Person
    {
        // MixinType: LogData
        public void Log(string data) => Console.WriteLine(data);
    }
}
// <auto-generated>
// This code was generated by a tool.
// Name: Minerals.AutoMixins
// Version: 0.2.1+6c5634e46b130effbe00bd9d3f94459f1dbb2e85
// </auto-generated>
#pragma warning disable CS9113
namespace Minerals.AutoMixins
{
    [global::System.Diagnostics.DebuggerNonUserCode]
    [global::System.Runtime.CompilerServices.CompilerGenerated]
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
    [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
    public sealed class AddMixinAttribute : global::System.Attribute
    {
        public AddMixinAttribute(params global::System.Type[] mixins)
        {
        }
    }
}
#pragma warning restore CS9113
// <auto-generated>
// This code was generated by a tool.
// Name: Minerals.AutoMixins
// Version: 0.2.1+6c5634e46b130effbe00bd9d3f94459f1dbb2e85
// </auto-generated>
namespace Minerals.AutoMixins
{
    [global::System.Diagnostics.DebuggerNonUserCode]
    [global::System.Runtime.CompilerServices.CompilerGenerated]
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
    [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
    public sealed class GenerateMixinAttribute : global::System.Attribute
    {
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Minerals.AutoMixins

RSCG – ThisClass

 
 

name ThisClass
nuget https://www.nuget.org/packages/ThisClass/
link https://github.com/trympet/ThisClass
author Trym Lund Flogard

Generate full class name from class

 

This is how you can use ThisClass .

The code that you start with is


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

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

	<ItemGroup>
		<PackageReference Include="ThisClass" Version="1.5.11" />
	</ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

</Project>


The code that you will use is


using DemoClass;

Person person = new Person();
person.Name = "Andrei Ignat";
Console.WriteLine(person.Name);
Console.WriteLine(Person.ThisClass.FullName);



namespace DemoClass;
[ThisClass]
internal partial class Person
{
    public string Name { get; set; }

    public string ClassName => ThisClass.FullName;
}


 

The code that is generated is

// <auto-generated/>
#pragma warning disable CS0108, CS1591, CS1573, CS0465, CS0649, CS8019, CS1570, CS1584, CS1658, CS0436, CS8981
#nullable enable
namespace DemoClass;
partial class Person
{
    public static partial class ThisClass
    {
        /// <summary>
        /// Gets the fully qualified name of the parent class, including the namespace but not the assembly.
        /// </summary>
        public const string FullName = "DemoClass.Person";
    }
}
// <auto-generated/>
#pragma warning disable CS1591,CS1573,CS0465,CS0649,CS8019,CS1570,CS1584,CS1658,CS0436,CS8981
using System;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false, AllowMultiple = true)]
sealed partial class ThisClassAttribute : Attribute
{
}

Code and pdf at

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

RSCG – RossLean.StringificationGenerator

 
 

name RossLean.StringificationGenerator
nuget https://www.nuget.org/packages/RossLean.StringificationGenerator/
link https://github.com/RossLean/RossLean/
author Alex Kalfakakos

Generating constructor code as string

 

This is how you can use RossLean.StringificationGenerator .

The code that you start with is


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

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

  <ItemGroup>
    <PackageReference Include="RossLean.StringificationGenerator" Version="1.0.1">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="RossLean.StringificationGenerator.Core" Version="1.0.0" />
  </ItemGroup>

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




The code that you will use is


using Code2String;
using RossLean.StringificationGenerator.Generated;
Person person = new Person("Andrei", "Ignat");
string personString = ConstructionCodeGeneration.ForPerson(person);
Console.WriteLine(personString);


using RossLean.StringificationGenerator.Core;

namespace Code2String;
[GenerateConstructionCode]
public record Person(string firstName, string lastName)
{
    
}


 

The code that is generated is

using Code2String;
using RossLean.StringificationGenerator.Core;
using System.Text;

namespace RossLean.StringificationGenerator.Generated;

public partial class ConstructionCodeGeneration : BaseConstructionCodeGeneration
{
    public static string ForPerson(Person person)
    {
        return $$"""
            new Person({{StringLiteral(person.firstName)}}, {{StringLiteral(person.lastName)}})
            """;
    }
    public static void AppendPerson(Person person, StringBuilder builder)
    {
        builder.Append($$"""
            new Person({{StringLiteral(person.firstName)}}, {{StringLiteral(person.lastName)}})
            """);
    }
    public static string ForPersonTargetTyped(Person person)
    {
        return $$"""
            new({{StringLiteral(person.firstName)}}, {{StringLiteral(person.lastName)}})
            """;
    }
    public static void AppendPersonTargetTyped(Person person, StringBuilder builder)
    {
        builder.Append($$"""
            new({{StringLiteral(person.firstName)}}, {{StringLiteral(person.lastName)}})
            """);
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/RossLean.StringificationGenerator

RSCG – Minerals.AutoInterfaces

 
 

name Minerals.AutoInterfaces
nuget https://www.nuget.org/packages/Minerals.AutoInterfaces/
link https://github.com/SzymonHalucha/Minerals.AutoInterfaces
author Szymon HaƂucha

Generating interface from class

 

This is how you can use Minerals.AutoInterfaces .

The code that you start with is


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

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

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

	<ItemGroup>
	  <PackageReference Include="Minerals.AutoInterfaces" Version="0.1.5" />
	</ItemGroup>
</Project>


The code that you will use is


// See https://aka.ms/new-console-template for more information
using Class2Interface;

Console.WriteLine("Hello, World!");
IPerson person=new Person();
person.FirstName="Andrei";
person.LastName="Ignat";
Console.WriteLine(person.FullName());


namespace Class2Interface;
[Minerals.AutoInterfaces.GenerateInterface("IPerson")]
public partial class Person:IPerson
{
    public int ID { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public string Name
    {
        get
        {
            return $"{FirstName} {LastName}";
        }
    }
    public string FullName()=>$"{FirstName} {LastName}";
}


 

The code that is generated is

// <auto-generated>
// This code was generated by a tool.
// Name: Minerals.AutoInterfaces
// Version: 0.1.5+54d6efe308ef06f041fc9b5d9285caeecef3e7c4
// </auto-generated>
#pragma warning disable CS9113
namespace Minerals.AutoInterfaces
{
    [global::System.Diagnostics.DebuggerNonUserCode]
    [global::System.Runtime.CompilerServices.CompilerGenerated]
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
    [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
    public sealed class GenerateInterfaceAttribute : global::System.Attribute
    {
        public GenerateInterfaceAttribute(string customName = "")
        {
        }
    }
}
#pragma warning restore CS9113
// <auto-generated>
// This code was generated by a tool.
// Name: Minerals.AutoInterfaces
// Version: 0.1.5+54d6efe308ef06f041fc9b5d9285caeecef3e7c4
// </auto-generated>
namespace Class2Interface
{
    [global::System.Runtime.CompilerServices.CompilerGenerated]
    public interface IPerson
    {
        int ID { get; set; }
        string? FirstName { get; set; }
        string? LastName { get; set; }
        string Name { get; }
        string FullName();
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Minerals.AutoInterfaces

RSCG – MinimalApis.Discovery

name MinimalApis.Discovery
nuget https://www.nuget.org/packages/MinimalApis.Discovery/
link https://github.com/shawnwildermuth/MinimalApis
author Shawn Wildermuth

Controller like API registering

 

This is how you can use MinimalApis.Discovery .

The code that you start with is


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

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.4" />
    <PackageReference Include="MinimalApis.Discovery" Version="1.0.7" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using Microsoft.AspNetCore.Http.HttpResults;
using MinimalApis.Discovery;
namespace APIDemo;

public class PersonAPI : IApi
{
    public void Register(IEndpointRouteBuilder builder)
    {
        var grp = builder.MapGroup("/api/Person");
        grp.MapGet("", GetFromId);
        grp.MapGet("{id:int}", GetFromId);
        //todo: add more routes
    }
    public static async Task<Person[]> GetAll()
    {       
        await Task.Delay(1000);
        return new[] { new Person { FirstName = "Ignat", LastName = "Andrei" } };
    }

    public static async Task<Results<Ok<Person>,NotFound<string>>> GetFromId(int id)
    {
        await Task.Delay(1000);
        if (id == 1)
        {
            return TypedResults.Ok<Person>(new Person { FirstName = "Ignat", LastName = "Andrei" });
        }
        return TypedResults.NotFound<string>("Person not found");
    }
}



namespace APIDemo;

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



using MinimalApis.Discovery;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

//app.UseHttpsRedirection();

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();

app.MapApis();

app.Run();

internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}


 

The code that is generated is

// <auto-generated/>
using System;
using Microsoft.AspNetCore.Builder;

namespace MinimalApis.Discovery
{
  public static class MinimalApisDiscoveryGeneratedExtensions
  {
    public static WebApplication MapApis(this WebApplication app)
    {
      // Call Register on all classes that implement IApi
      new global::APIDemo.PersonAPI().Register(app);
      return app;
    }
  }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/MinimalApis.Discovery

RSCG – BitsKit

name BitsKit
nuget https://www.nuget.org/packages/BitsKit/
link https://github.com/barncastle/BitsKit
author barncastle

Reading efficiently from a bit structure

 

This is how you can use BitsKit .

The code that you start with is


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

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

  <ItemGroup>
    <PackageReference Include="BitsKit" Version="1.0.0" />
  </ItemGroup>

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

</Project>


The code that you will use is


using BitsDemo;

var z = new zlib_header(0x78, 0x9C);
Console.WriteLine( z.FLEVEL );
Console.WriteLine(z.CM);


using BitsKit;
using BitsKit.BitFields;
using System.IO.Compression;

namespace BitsDemo;
//[BitObject(BitOrder.LeastSignificant)]
//partial struct zlib_header
//{
//    public zlib_header(byte cmf, byte flg)
//    {
//        CMF = cmf;
//        FLG = flg;
//    }
//    [EnumField("CM", 4, typeof(CompressionMode))]
//    [BitField("CINFO", 4)]
//    private byte CMF;

//    [BitField("FCHECK", 5)]
//    [BooleanField("FDICT")]
//    [EnumField("FLEVEL", 2, typeof(CompressionLevel))]
//    private byte FLG;
//}

[BitObject(BitOrder.LeastSignificant)]
partial struct zlib_header
{
    public zlib_header(byte cmf, byte flg)
    {
        CMF = cmf;
        FLG = flg;
    }

    [BitField("CM", 4)]
    [BitField("CINFO", 4)]
    private byte CMF;

    [BitField("FCHECK", 5)]
    [BitField("FDICT", 1)]
    [BitField("FLEVEL", 2)]
    private byte FLG;
}

 

The code that is generated is

#nullable enable
#pragma warning disable IDE0005 // Using directive is unnecessary
#pragma warning disable IDE0005_gen // Using directive is unnecessary
#pragma warning disable CS8019  // Unnecessary using directive.
#pragma warning disable IDE0161 // Convert to file-scoped namespace

using System;
using System.Runtime.InteropServices;
using BitsKit.Primitives;

namespace BitsDemo
{
    partial struct  zlib_header
    {
        public  Byte CM 
        {
            get => BitPrimitives.ReadUInt8LSB(CMF, 0, 4);
            set => BitPrimitives.WriteUInt8LSB(ref CMF, 0, value, 4);
        }

        public  Byte CINFO 
        {
            get => BitPrimitives.ReadUInt8LSB(CMF, 4, 4);
            set => BitPrimitives.WriteUInt8LSB(ref CMF, 4, value, 4);
        }

        public  Byte FCHECK 
        {
            get => BitPrimitives.ReadUInt8LSB(FLG, 0, 5);
            set => BitPrimitives.WriteUInt8LSB(ref FLG, 0, value, 5);
        }

        public  Byte FDICT 
        {
            get => BitPrimitives.ReadUInt8LSB(FLG, 5, 1);
            set => BitPrimitives.WriteUInt8LSB(ref FLG, 5, value, 1);
        }

        public  Byte FLEVEL 
        {
            get => BitPrimitives.ReadUInt8LSB(FLG, 6, 2);
            set => BitPrimitives.WriteUInt8LSB(ref FLG, 6, value, 2);
        }
    }
}

Code and pdf at

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

RSCG – FusionReactor

name FusionReactor
nuget https://www.nuget.org/packages/FusionReactor.SourceGenerators.EnumExtensions
link https://github.com/OhFlowi/FusionReactor.SourceGenerators.EnumExtensions
author OhFlowi

Enums to string and other extensions

 

This is how you can use FusionReactor .

The code that you start with is


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

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

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

  <ItemGroup>
    <PackageReference Include="FusionReactor.SourceGenerators.EnumExtensions" Version="1.1.0" />
  </ItemGroup>

  
</Project>


The code that you will use is


using EnumClassDemo;
Console.WriteLine(Colors.None.GetName());
Console.WriteLine(ColorsExtensions.GetContent()[Colors.None]);


using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;

namespace EnumClassDemo;

//[Flags]
[FusionReactor.SourceGenerators.EnumExtensions.GenerateEnumExtensions]
public enum Colors
{
    [Display(
         ShortName = "None",
         Name = "none - 0",
         Description = "Zero",
         Prompt = "ooF",
         GroupName = "Color1",
         Order = 0)]
    None =0,
    Red=1,
    Green=2,
    Blue=4,
}


 

The code that is generated is

// <auto-generated />
#nullable enable
using System;
using System.CodeDom.Compiler;
using System.Collections;
#if NET8_0_OR_GREATER
using System.Collections.Frozen;
#endif
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace EnumClassDemo;
/// <summary>
/// Extension methods for the <see cref = "Colors"/> enum.
/// </summary>
[GeneratedCode("FusionReactor.SourceGenerators.EnumExtensions", null)]
public static partial class ColorsExtensions
{
#if NET8_0_OR_GREATER
private static readonly FrozenDictionary<Colors, Int32> content
  = new Dictionary<Colors, Int32>
    {
        { Colors.None, 0 },
{ Colors.Red, 1 },
{ Colors.Green, 2 },
{ Colors.Blue, 4 },

    }
    .ToFrozenDictionary();
#else
    private static readonly Dictionary<Colors, Int32> contentDictionary = new Dictionary<Colors, Int32>
    {
        {
            Colors.None,
            0
        },
        {
            Colors.Red,
            1
        },
        {
            Colors.Green,
            2
        },
        {
            Colors.Blue,
            4
        },
    };
    private static readonly IReadOnlyDictionary<Colors, Int32> content = new ReadOnlyDictionary<Colors, Int32>(contentDictionary);
#endif
#if NET8_0_OR_GREATER
private static readonly FrozenSet<string> names = new []
{
    "None",
"Red",
"Green",
"Blue",

}
.ToFrozenSet();
#elif NET5_0_OR_GREATER
private static readonly IReadOnlySet<string> names = new HashSet<string>()
{
    "None",
"Red",
"Green",
"Blue",

};
#else
    private static readonly HashSet<string> names = new HashSet<string>()
    {
        "None",
        "Red",
        "Green",
        "Blue",
    };
#endif
#if NET8_0_OR_GREATER
private static readonly FrozenSet<Colors> values = new []
{
    Colors.None,
Colors.Red,
Colors.Green,
Colors.Blue,

}
.ToFrozenSet();
#elif NET5_0_OR_GREATER
private static readonly IReadOnlySet<Colors> values = new HashSet<Colors>()
{
    Colors.None,
Colors.Red,
Colors.Green,
Colors.Blue,

};
#else
    private static readonly HashSet<Colors> values = new HashSet<Colors>()
    {
        Colors.None,
        Colors.Red,
        Colors.Green,
        Colors.Blue,
    };
#endif
    /// <summary>
    /// Gets the content dictionary containing mappings of <see cref = "Colors"/> enum values to values.
    /// </summary>
    /// <returns>The read-only content dictionary.</returns>
    
#if NET8_0_OR_GREATER
public static FrozenDictionary<Colors, Int32> GetContent()
#else
    public static IReadOnlyDictionary<Colors, Int32> GetContent()
#endif
    {
        return content;
    }

    /// <summary>
    /// Gets the content dictionary containing mappings of <see cref = "Colors"/> enum values to values.
    /// </summary>
    /// <param name = "enumValue">The enum value for which to get the content dictionary.</param>
    /// <returns>The read-only content dictionary.</returns>
    
#if NET8_0_OR_GREATER
public static FrozenDictionary<Colors, Int32> GetContent(this Colors enumValue)
#else
    public static IReadOnlyDictionary<Colors, Int32> GetContent(this Colors enumValue)
#endif
    {
        return content;
    }

    /// <summary>
    /// Retrieves the name of the constant in the <see cref = "Colors"/>.
    /// </summary>
    /// <param name = "enumValue">The enum value to convert.</param>
    /// <returns>
    /// A string containing the name of the <see cref = "Colors"/>;
    /// or <see langword="null"/> if no such constant is found.
    /// </returns>
    public static string? GetName(this Colors enumValue)
    {
        return enumValue switch
        {
            Colors.None => nameof(Colors.None),
            Colors.Red => nameof(Colors.Red),
            Colors.Green => nameof(Colors.Green),
            Colors.Blue => nameof(Colors.Blue),
            _ => null
        };
    }

    /// <summary>
    /// Retrieves all available names of the <see cref = "Colors"/>.
    /// </summary>
    /// <returns>An enumerable collection of <see cref = "Colors"/> names.</returns>
    
#if NET8_0_OR_GREATER
public static FrozenSet<string> GetNames()
#elif NET5_0_OR_GREATER
public static IReadOnlySet<string> GetNames()
#else
    public static HashSet<string> GetNames()
#endif
    {
        return names;
    }

    /// <summary>
    /// Retrieves all available names of the <see cref = "Colors"/>.
    /// </summary>
    /// <param name = "enumValue">The enumeration value.</param>
    /// <returns>An enumerable collection of <see cref = "Colors"/> names.</returns>
    
#if NET8_0_OR_GREATER
public static FrozenSet<string> GetNames(this Colors enumValue)
#elif NET5_0_OR_GREATER
public static IReadOnlySet<string> GetNames(this Colors enumValue)
#else
    public static HashSet<string> GetNames(this Colors enumValue)
#endif
    {
        return names;
    }

    /// <summary>
    /// Retrieves all available values of the <see cref = "Colors"/>.
    /// </summary>
    /// <returns>An enumerable collection of <see cref = "Colors"/> values.</returns>
    
#if NET8_0_OR_GREATER
public static FrozenSet<Colors> GetValues()
#elif NET5_0_OR_GREATER
public static IReadOnlySet<Colors> GetValues()
#else
    public static HashSet<Colors> GetValues()
#endif
    {
        return values;
    }

    /// <summary>
    /// Retrieves all available values of the <see cref = "Colors"/>.
    /// </summary>
    /// <param name = "enumValue">The enumeration value.</param>
    /// <returns>An enumerable collection of <see cref = "Colors"/> values.</returns>
    
#if NET8_0_OR_GREATER
public static FrozenSet<Colors> GetValues(this Colors enumValue)
#elif NET5_0_OR_GREATER
public static IReadOnlySet<Colors> GetValues(this Colors enumValue)
#else
    public static HashSet<Colors> GetValues(this Colors enumValue)
#endif
    {
        return values;
    }

    /// <summary>
    /// Parses the specified string representation of the enumeration value to its corresponding
    /// <see cref = "Colors"/> value.
    /// </summary>
    /// <param name = "value">A string containing the name or value to convert.</param>
    /// <param name = "ignoreCase">
    /// A boolean indicating whether to ignore case during the parsing. Default is <c>false</c>.
    /// </param>
    /// <returns>
    /// The <see cref = "Colors"/> value equivalent to the specified string representation.
    /// </returns>
    public static Colors Parse(string value, bool ignoreCase = false)
    {
        if (ignoreCase)
        {
            return value.ToLowerInvariant() switch
            {
                "none" => Colors.None,
                "red" => Colors.Red,
                "green" => Colors.Green,
                "blue" => Colors.Blue,
                _ => throw new ArgumentException(),
            };
        }
        else
        {
            return value switch
            {
                "None" => Colors.None,
                "Red" => Colors.Red,
                "Green" => Colors.Green,
                "Blue" => Colors.Blue,
                _ => throw new ArgumentException(),
            };
        }
    }

    /// <summary>
    /// Parses the specified string representation of the enumeration value to its corresponding
    /// <see cref = "Colors"/> value.
    /// </summary>
    /// <param name = "enumValue">The current <see cref = "Colors"/> value.</param>
    /// <param name = "value">A string containing the name or value to convert.</param>
    /// <param name = "ignoreCase">
    /// A boolean indicating whether to ignore case during the parsing. Default is <c>false</c>.
    /// </param>
    /// <returns>
    /// The <see cref = "Colors"/> value equivalent to the specified string representation.
    /// </returns>
    public static Colors Parse(this Colors enumValue, string value, bool ignoreCase = false)
    {
        if (ignoreCase)
        {
            return value.ToLowerInvariant() switch
            {
                "none" => Colors.None,
                "red" => Colors.Red,
                "green" => Colors.Green,
                "blue" => Colors.Blue,
                _ => throw new ArgumentException(),
            };
        }
        else
        {
            return value switch
            {
                "None" => Colors.None,
                "Red" => Colors.Red,
                "Green" => Colors.Green,
                "Blue" => Colors.Blue,
                _ => throw new ArgumentException(),
            };
        }
    }

    /// <summary>
    /// Tries to parse the specified string representation of an enumeration value to its corresponding
    /// <see cref = "Colors"/> enumeration value.
    /// </summary>
    /// <param name = "value">The string representation of the enumeration value.</param>
    /// <param name = "result">
    /// When this method returns, contains the <see cref = "Colors"/> value equivalent
    /// to the string representation, if the parse succeeded, or default(Colors) if the parse failed.</param>
    /// <returns><c>true</c> if the parsing was successful; otherwise, <c>false</c>.</returns>
    public static bool TryParse(string value, out Colors? result)
    {
        return TryParse(value, false, out result);
    }

    /// <summary>
    /// Tries to parse the specified string representation of an enumeration value to its corresponding
    /// <see cref = "Colors"/> enumeration value.
    /// </summary>
    /// <param name = "value">The string representation of the enumeration value.</param>
    /// <param name = "ignoreCase">A boolean indicating whether case should be ignored when parsing.</param>
    /// <param name = "result">
    /// When this method returns, contains the <see cref = "Colors"/> value equivalent
    /// to the string representation, if the parse succeeded, or default(Colors) if the parse failed.</param>
    /// <returns><c>true</c> if the parsing was successful; otherwise, <c>false</c>.</returns>
    public static bool TryParse(string value, bool ignoreCase, out Colors? result)
    {
        if (ignoreCase)
        {
            result = value.ToLowerInvariant() switch
            {
                "none" => Colors.None,
                "red" => Colors.Red,
                "green" => Colors.Green,
                "blue" => Colors.Blue,
                _ => null,
            };
        }
        else
        {
            result = value switch
            {
                "None" => Colors.None,
                "Red" => Colors.Red,
                "Green" => Colors.Green,
                "Blue" => Colors.Blue,
                _ => null,
            };
        }

        return result != null;
    }

    /// <summary>
    /// Tries to parse the specified string representation of an enumeration value to its corresponding
    /// <see cref = "Colors"/> enumeration value.
    /// </summary>
    /// <param name = "enumValue">The enumeration value to parse.</param>
    /// <param name = "value">The string representation of the enumeration value.</param>
    /// <param name = "result">
    /// When this method returns, contains the <see cref = "Colors"/> value equivalent
    /// to the string representation, if the parse succeeded, or default(Colors) if the parse failed.</param>
    /// <returns><c>true</c> if the parsing was successful; otherwise, <c>false</c>.</returns>
    public static bool TryParse(this Colors enumValue, string value, out Colors? result)
    {
        return TryParse(value, false, out result);
    }

    /// <summary>
    /// Tries to parse the specified string representation of an enumeration value to its corresponding
    /// <see cref = "Colors"/> enumeration value.
    /// </summary>
    /// <param name = "enumValue">The enumeration value to parse.</param>
    /// <param name = "value">The string representation of the enumeration value.</param>
    /// <param name = "ignoreCase">A boolean indicating whether case should be ignored when parsing.</param>
    /// <param name = "result">
    /// When this method returns, contains the <see cref = "Colors"/> value equivalent
    /// to the string representation, if the parse succeeded, or default(Colors) if the parse failed.</param>
    /// <returns><c>true</c> if the parsing was successful; otherwise, <c>false</c>.</returns>
    public static bool TryParse(this Colors enumValue, string value, bool ignoreCase, out Colors? result)
    {
        if (ignoreCase)
        {
            result = value.ToLowerInvariant() switch
            {
                "none" => Colors.None,
                "red" => Colors.Red,
                "green" => Colors.Green,
                "blue" => Colors.Blue,
                _ => null,
            };
        }
        else
        {
            result = value switch
            {
                "None" => Colors.None,
                "Red" => Colors.Red,
                "Green" => Colors.Green,
                "Blue" => Colors.Blue,
                _ => null,
            };
        }

        return result != null;
    }
}
// <auto-generated />
#nullable enable
using System;
using System.Collections;
#if NET8_0_OR_GREATER
using System.Collections.Frozen;
#endif
using System.Collections.Generic;
using System.Collections.ObjectModel;
using FusionReactor.SourceGenerators.EnumExtensions;

namespace EnumClassDemo;
public static partial class ColorsExtensions
{
#if !NET8_0_OR_GREATER
    private static readonly Dictionary<Colors, DisplayResult?> displayResultsDictionary = new Dictionary<Colors, DisplayResult?>
    {
        {
            Colors.None,
            new DisplayResult
            {
                ShortName = "None",
                Name = "none - 0",
                Description = "Zero",
                Prompt = "ooF",
                GroupName = "Color1",
                Order = 0,
            }
        },
        {
            Colors.Red,
            null
        },
        {
            Colors.Green,
            null
        },
        {
            Colors.Blue,
            null
        },
    };
#endif
    /// <summary>
    /// Returns the <see cref = "System.ComponentModel.DataAnnotations.DisplayAttribute"/> of the <see cref = "Colors"/> enum.
    /// </summary>
    /// <returns>The display attribute result or the enum value.</returns>
    
#if NET8_0_OR_GREATER
public static FrozenDictionary<Colors, DisplayResult?> DisplayResults
  => new Dictionary<Colors, DisplayResult?>
    {
        { Colors.None, new DisplayResult {ShortName = "None",
Name = "none - 0",
Description = "Zero",
Prompt = "ooF",
GroupName = "Color1",
Order = 0,
}},
{ Colors.Red, null },
{ Colors.Green, null },
{ Colors.Blue, null },

    }
    .ToFrozenDictionary();
#else
    public static IReadOnlyDictionary<Colors, DisplayResult?> DisplayResults => new ReadOnlyDictionary<Colors, DisplayResult?>(displayResultsDictionary);

#endif
    /// <summary>
    /// Returns the <see cref = "System.ComponentModel.DataAnnotations.DisplayAttribute.ShortName"/> of the <see cref = "Colors"/> enum.
    /// </summary>
    /// <param name = "enumValue">The enum value.</param>
    /// <returns>The display name or the enum value.</returns>
    public static string? DisplayShortName(this Colors enumValue)
    {
        return enumValue switch
        {
            Colors.None => "None",
            Colors.Red => null,
            Colors.Green => null,
            Colors.Blue => null,
            _ => null
        };
    }

    /// <summary>
    /// Returns the <see cref = "System.ComponentModel.DataAnnotations.DisplayAttribute.Name"/> of the <see cref = "Colors"/> enum.
    /// </summary>
    /// <param name = "enumValue">The enum value.</param>
    /// <returns>The name or the enum value.</returns>
    public static string? DisplayName(this Colors enumValue)
    {
        return enumValue switch
        {
            Colors.None => "none - 0",
            Colors.Red => null,
            Colors.Green => null,
            Colors.Blue => null,
            _ => null
        };
    }

    /// <summary>
    /// Returns the <see cref = "System.ComponentModel.DataAnnotations.DisplayAttribute.Description"/> of the <see cref = "Colors"/> enum.
    /// </summary>
    /// <param name = "enumValue">The enum value.</param>
    /// <returns>The display name or the enum value.</returns>
    public static string? DisplayDescription(this Colors enumValue)
    {
        return enumValue switch
        {
            Colors.None => "Zero",
            Colors.Red => null,
            Colors.Green => null,
            Colors.Blue => null,
            _ => null
        };
    }

    /// <summary>
    /// Returns the <see cref = "System.ComponentModel.DataAnnotations.DisplayAttribute.Prompt"/> of the <see cref = "Colors"/> enum.
    /// </summary>
    /// <param name = "enumValue">The enum value.</param>
    /// <returns>The display name or the enum value.</returns>
    public static string? DisplayPrompt(this Colors enumValue)
    {
        return enumValue switch
        {
            Colors.None => "ooF",
            Colors.Red => null,
            Colors.Green => null,
            Colors.Blue => null,
            _ => null
        };
    }

    /// <summary>
    /// Returns the <see cref = "System.ComponentModel.DataAnnotations.DisplayAttribute.GroupName"/> of the <see cref = "Colors"/> enum.
    /// </summary>
    /// <param name = "enumValue">The enum value.</param>
    /// <returns>The display name or the enum value.</returns>
    public static string? DisplayGroupName(this Colors enumValue)
    {
        return enumValue switch
        {
            Colors.None => "Color1",
            Colors.Red => null,
            Colors.Green => null,
            Colors.Blue => null,
            _ => null
        };
    }

    /// <summary>
    /// Returns the <see cref = "System.ComponentModel.DataAnnotations.DisplayAttribute.Order"/> of the <see cref = "Colors"/> enum.
    /// </summary>
    /// <param name = "enumValue">The enum value.</param>
    /// <returns>The display name or the enum value.</returns>
    public static int? DisplayOrder(this Colors enumValue)
    {
        return enumValue switch
        {
            Colors.None => 0,
            Colors.Red => null,
            Colors.Green => null,
            Colors.Blue => null,
            _ => null
        };
    }
}
// <auto-generated />
#nullable enable
using System;
using System.CodeDom.Compiler;

namespace FusionReactor.SourceGenerators.EnumExtensions;
/// <inheritdoc cref = "System.ComponentModel.DataAnnotations.DisplayAttribute"/>
[GeneratedCode("FusionReactor.SourceGenerators.EnumExtensions", null)]
public class DisplayResult
{
    /// <summary>
    /// Gets or sets the ShortName attribute property, which may be a resource key string.
    /// </summary>
    public string? ShortName { get; set; }
    /// <summary>
    /// Gets or sets the Name attribute property, which may be a resource key string.
    /// </summary>
    public string? Name { get; set; }
    /// <summary>
    /// Gets or sets the Description attribute property, which may be a resource key string.
    /// </summary>
    public string? Description { get; set; }
    /// <summary>
    /// Gets or sets the Prompt attribute property, which may be a resource key string.
    /// </summary>
    public string? Prompt { get; set; }
    /// <summary>
    /// Gets or sets the GroupName attribute property, which may be a resource key string.
    /// </summary>
    public string? GroupName { get; set; }
    /// <summary>
    /// Gets or sets the order in which this field should be displayed.  If this property is not set then
    /// the presentation layer will automatically determine the order.  Setting this property explicitly
    /// allows an override of the default behavior of the presentation layer.
    /// </summary>
    public int? Order { get; set; }
}
// <auto-generated />
using System;
using System.CodeDom.Compiler;

namespace FusionReactor.SourceGenerators.EnumExtensions;
/// <summary>
/// Attribute to mark an enum for FusionReactor.SourceGenerators.EnumExtensions extension generations.
/// </summary>
/// <remarks>
/// This attribute is used to mark an enum for FusionReactor.SourceGenerators.EnumExtensions extension generations.
/// </remarks>
[GeneratedCode("FusionReactor.SourceGenerators.EnumExtensions", null)]
[AttributeUsage(AttributeTargets.Enum)]
public class GenerateEnumExtensionsAttribute : Attribute
{
}

Code and pdf at

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

RSCG – UnionGen

name UnionGen
nuget https://www.nuget.org/packages/UnionGen/
link https://github.com/markushaslinger/union_source_generator
author M. Haslinger

Generating unions between types

 

This is how you can use UnionGen .

The code that you start with is


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

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

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

	<ItemGroup>
	  <PackageReference Include="UnionGen" Version="1.4.0" />
	</ItemGroup>

</Project>


The code that you will use is


using UnionTypesDemo;

Console.WriteLine("Save or not");
var data = SaveToDatabase.Save(0);
Console.WriteLine(data.IsNotFound);
data = SaveToDatabase.Save(1);
Console.WriteLine(data.IsResultOfInt32);

Console.WriteLine(data.AsResultOfInt32());



using UnionGen.Types;
using UnionGen;
namespace UnionTypesDemo;

[Union<Result<int>, NotFound>]
public partial struct ResultSave
{
}





using UnionGen.Types;

namespace UnionTypesDemo;

public class SaveToDatabase
{
    public static ResultSave Save(int i)
    {
        if(i ==0)
        {
            return new NotFound();
        }
        return new Result<int>(i);
    }
}




 

The code that is generated is

// <auto-generated by UnionSourceGen />
#nullable enable
using System;
namespace UnionTypesDemo
{

    public readonly partial struct ResultSave : IEquatable<ResultSave>
    {
		private readonly UnionGen.Types.Result<int> _value0;
		private readonly UnionGen.Types.NotFound _value1;
		private readonly UnionGen.InternalUtil.StateByte _state;

		private ResultSave(int index, int actualTypeIndex)
		{
			_state = new UnionGen.InternalUtil.StateByte(index, actualTypeIndex);
		}

		public ResultSave(UnionGen.Types.Result<int> value): this(0, 0)
		{
			_value0 = value;
		}

		public ResultSave(UnionGen.Types.NotFound value): this(1, 1)
		{
			_value1 = value;
		}

		[Obsolete(UnionGen.InternalUtil.UnionGenInternalConst.DefaultConstructorWarning, true)]
		public ResultSave(): this(0, 0) {}

		public bool IsResultOfInt32 => _state.Index == 0;
		public bool IsNotFound => _state.Index == 1;

		public UnionGen.Types.Result<int> AsResultOfInt32() =>
			IsResultOfInt32
				? _value0
				: throw UnionGen.InternalUtil.ExceptionHelper.ThrowNotOfType(GetTypeName(0), GetTypeName(_state.ActualTypeIndex));
		
		public UnionGen.Types.NotFound AsNotFound() =>
			IsNotFound
				? _value1
				: throw UnionGen.InternalUtil.ExceptionHelper.ThrowNotOfType(GetTypeName(1), GetTypeName(_state.ActualTypeIndex));

		public static implicit operator ResultSave(UnionGen.Types.Result<int> value) => new ResultSave(value);
		public static implicit operator ResultSave(UnionGen.Types.NotFound value) => new ResultSave(value);
		public static bool operator ==(ResultSave left, ResultSave right) => left.Equals(right);
		public static bool operator !=(ResultSave left, ResultSave right) => !left.Equals(right);

		public TResult Match<TResult>(Func<UnionGen.Types.Result<int>, TResult> withResultOfInt32, Func<UnionGen.Types.NotFound, TResult> withNotFound) => 		
			_state.ActualTypeIndex switch
			{
				0 => withResultOfInt32(_value0),
				1 => withNotFound(_value1),
				_ => throw UnionGen.InternalUtil.ExceptionHelper.ThrowUnknownTypeIndex(_state.ActualTypeIndex)
			};

		public void Switch(Action<UnionGen.Types.Result<int>> forResultOfInt32, Action<UnionGen.Types.NotFound> forNotFound)		
		{
			switch (_state.ActualTypeIndex)
			{
				case 0: forResultOfInt32(_value0); break;
				case 1: forNotFound(_value1); break;
				default: throw UnionGen.InternalUtil.ExceptionHelper.ThrowUnknownTypeIndex(_state.ActualTypeIndex);
			}
		}

		public override string ToString() => 		
			_state.Index switch
			{
				0 => _value0.ToString()!,
				1 => _value1.ToString()!,
				_ => throw UnionGen.InternalUtil.ExceptionHelper.ThrowUnknownTypeIndex(_state.Index)
			};

		public bool Equals(ResultSave other) => 
			_state.Index == other._state.Index
				&& _state.Index switch 
				{
					0 => _value0.Equals(other._value0),
					1 => _value1.Equals(other._value1),
					_ => false
				};

		public override bool Equals(object? obj)
		{
			if (ReferenceEquals(null, obj))
			{
				return false;
			}
			return obj is ResultSave other && Equals(other);
		}

		public override int GetHashCode(){		
			unchecked
			{
				var hash = _state.Index switch
				{
					0 => _value0.GetHashCode(),
					1 => _value1.GetHashCode(),
					_ => 0
				};
				return (hash * 397) ^ _state.Index;
			}
		}

		public string GetTypeName(int index) =>
			index switch 
			{
				0 => "UnionGen.Types.Result<int>",
				1 => "UnionGen.Types.NotFound",
				_ => throw UnionGen.InternalUtil.ExceptionHelper.ThrowUnknownTypeIndex(index)
			};

    }

}

Code and pdf at

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

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.