Category: roslyn

RSCG – QuickConstructor

RSCG – QuickConstructor
 
 

name QuickConstructor
nuget https://www.nuget.org/packages/QuickConstructor
link https://github.com/flavien/QuickConstructor
author Flavien Charlon

Fast add constructors that are read only FIELDS

Has multiple other features

 

This is how you can use QuickConstructor .

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="QuickConstructor" Version="1.0.5" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using QuickConstructorDemo;

var p = new Person("Andrei", "Ignat");


using QuickConstructor.Attributes;

namespace QuickConstructorDemo;

[QuickConstructor]
internal partial class Person
{
    private readonly string FirstName;
    private readonly string? LastName;

    
}


 

The code that is generated is

/// <auto-generated>
/// This code was generated by the QuickConstructor source generator.
/// </auto-generated>

#nullable enable

namespace QuickConstructorDemo
{
    partial class Person
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Person" /> class.
        /// </summary>
        public Person(string @firstName, string? @lastName)
        {
            if (@firstName == null)
                throw new global::System.ArgumentNullException(nameof(@firstName));

            this.@FirstName = @firstName;
            this.@LastName = @lastName;
        }
    }
}

Code and pdf at

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

RSCG – System.Runtime.InteropServices

RSCG – System.Runtime.InteropServices
 
 

name System.Runtime.InteropServices
nuget https://www.nuget.org/packages/System.Runtime.InteropServices/
link https://learn.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke-source-generation
author Microsoft

Generate PInvoke calls

 

This is how you can use System.Runtime.InteropServices .

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>
		<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
	</PropertyGroup>
</Project>


The code that you will use is


WriteLine(MessageBoxW(IntPtr.Zero, "asd", "asd", 1));
//RSCG
WriteLine(MessageBoxW_LI(IntPtr.Zero, "asd", "asd", 1));



partial class DemoImport
{
    [DllImport("user32.dll", EntryPoint = "MessageBoxW", CharSet = CharSet.Unicode, SetLastError = true)]

    internal static extern int MessageBoxW(IntPtr hWnd, string lpText, string lpCaption, uint uType);


    [LibraryImport("user32.dll", EntryPoint = "MessageBoxW", SetLastError = true,
StringMarshalling = StringMarshalling.Utf16)]

    internal static partial int MessageBoxW_LI(IntPtr hWnd, string lpText, string lpCaption, uint uType);
}


global using static System.Console;
global using static DemoImport;
global using System.Runtime.InteropServices;


 

The code that is generated is

// <auto-generated/>
unsafe partial class DemoImport
{
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "7.0.8.17405")]
    [System.Runtime.CompilerServices.SkipLocalsInitAttribute]
    internal static partial int MessageBoxW_LI(nint hWnd, string lpText, string lpCaption, uint uType)
    {
        int __lastError;
        int __retVal;
        // Pin - Pin data in preparation for calling the P/Invoke.
        fixed (void* __lpText_native = &global::System.Runtime.InteropServices.Marshalling.Utf16StringMarshaller.GetPinnableReference(lpText))
        fixed (void* __lpCaption_native = &global::System.Runtime.InteropServices.Marshalling.Utf16StringMarshaller.GetPinnableReference(lpCaption))
        {
            System.Runtime.InteropServices.Marshal.SetLastSystemError(0);
            __retVal = __PInvoke(hWnd, (ushort*)__lpText_native, (ushort*)__lpCaption_native, uType);
            __lastError = System.Runtime.InteropServices.Marshal.GetLastSystemError();
        }

        System.Runtime.InteropServices.Marshal.SetLastPInvokeError(__lastError);
        return __retVal;
        // Local P/Invoke
        [System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "MessageBoxW", ExactSpelling = true)]
        static extern unsafe int __PInvoke(nint hWnd, ushort* lpText, ushort* lpCaption, uint uType);
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/System.Runtime.InteropServices

RSCG – AutoDeconstruct

 
 

name AutoDeconstruct
nuget https://www.nuget.org/packages/AutoDeconstruct
link https://github.com/JasonBock/AutoDeconstruct/blob/main/docs/Overview.md
author Jason Bock

Automatically add deconstruct for all types in an assembly

 

This is how you can use AutoDeconstruct .

The code that you start with is


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

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

  <ItemGroup>
    <PackageReference Include="AutoDeconstruct" Version="1.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
Console.WriteLine("Hello, World!");
var p = new Person();
p.FirstName = "Test";
p.LastName = "Ignat";
var (_, l, _ ) = p;
Console.WriteLine($"Last name is {l}");


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

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

[NoAutoDeconstruct]
public class TestPerson
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public string? Title { get; set; }

}

 

The code that is generated is

#nullable enable

public static partial class PersonExtensions
{
	public static void Deconstruct(this global::Person @self, out string? @firstName, out string? @lastName, out string? @title)
	{
		global::System.ArgumentNullException.ThrowIfNull(@self);
		(@firstName, @lastName, @title) =
			(@self.FirstName, @self.LastName, @self.Title);
	}
}

Code and pdf at

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

RSCG – RSCG_AMS

 
 

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

Automatically registering the version, ci, commit id

 

This is how you can use RSCG_AMS .

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="AMS_Base" Version="2023.5.21.1551" />
    <PackageReference Include="RSCG_AMS" Version="2023.5.21.1551" ReferenceOutputAssembly="false" OutputItemType="Analyzer" />
  </ItemGroup>

</Project>


The code that you will use is


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

Console.WriteLine("Hello, World!");

var amsAll = AboutMySoftware.AllDefinitions;
Console.WriteLine("Number definitions:" + amsAll?.Length);
foreach (var amsKV in amsAll)
{
    var ams = amsKV.Value;

    Console.WriteLine($"{amsKV.Key}.{nameof(ams.AssemblyName)} : {ams.AssemblyName}");
    Console.WriteLine($"{amsKV.Key}.{nameof(ams.DateGenerated)} : {ams.DateGenerated}");
    Console.WriteLine($"{amsKV.Key}.{nameof(ams.CommitId)} : {ams.CommitId}");
    Console.WriteLine($"{amsKV.Key}.{nameof(ams.RepoUrl)} : {ams.RepoUrl}");
    Console.WriteLine($"{amsKV.Key}.{nameof(ams.CISourceControl)} : {ams.CISourceControl}");
    Console.WriteLine($"{amsKV.Key}.{nameof(ams.Authors)} : {ams.Authors}");
    Console.WriteLine($"{amsKV.Key}.{nameof(ams.Version)} : {ams.Version}");
    Console.WriteLine($"{amsKV.Key}.{nameof(ams.User)} : {ams.User}");
    Console.WriteLine("versions" + ams.Versions?.Length);
    if (ams.Versions != null)
        foreach (var item in ams.Versions)
        {
            Console.WriteLine("release:" + item.Name);
            foreach (var merge in item.releaseDatas)
            {
                Console.WriteLine("=>merge:" + merge.Subject);
            }

        }
}




[assembly: AMS_Base.VersionReleased(Name = "WithVersioning", ISODateTime = "2022-04-02", recordData = AMS_Base.RecordData.Merges)]
[assembly: AMS_Base.VersionReleased(Name = "FutureRelease", ISODateTime = "9999-04-16", recordData = AMS_Base.RecordData.Merges)]


 

The code that is generated is

using System;
using AMS_Base;
namespace RSCG_AMSDemo { 
    /// <summary>
    /// this is the About My Software for 828469749970839076110119122
    /// </summary>
    public class XAboutMySoftware_828469749970839076110119122 :AboutMySoftware {
        /// <summary>
        /// starts when this module is loaded and 
        /// add the AMS tot the 
        /// </summary>
        [System.Runtime.CompilerServices.ModuleInitializer]
        public static void Add_AboutMySoftware_828469749970839076110119122(){
            AboutMySoftware.AddDefinition("RSCG_AMSDemo",new  XAboutMySoftware_828469749970839076110119122());      
        }
        /// <summary>
        /// constructor
        /// for AMS 828469749970839076110119122
        /// </summary>
        public XAboutMySoftware_828469749970839076110119122(){
            AssemblyName ="RSCG_AMSDemo" ; 
            DateGenerated = DateTime.ParseExact("20230521152410", "yyyyMMddHHmmss", null); 
            CommitId  = "not in a CI run" ; 
            RepoUrl ="https://ignatandrei.github.io/RSCG_AMS/runtimeMessages/NotFound.md" ; 
            CISourceControl = "not in a CI run" ; 
            SourceCommit = "https://ignatandrei.github.io/RSCG_AMS/runtimeMessages/NotFound.md" ; 
            Authors= "";
            Version= "";    
            EnvironmentVars =";User_Path;User_TEMP;User_OneDrive;User_OneDriveConsumer;User_OneDriveCommercial;User_POSH_THEMES_PATH;User_TMP;User_R_HOME;User_ChocolateyLastPathUpdate;Process_OneDriveConsumer;Process_ProgramFiles(x86);Process_ProgramW6432;Process_windir;Process_ChocolateyInstall;Process_PROCESSOR_IDENTIFIER;Process_POWERSHELL_DISTRIBUTION_CHANNEL;Process_TMP;Process_PROCESSOR_ARCHITECTURE;Process_Path;Process_PkgDefApplicationConfigFile;Process_USERPROFILE;Process_VisualStudioDir;Process_PROCESSOR_REVISION;Process_ChocolateyLastPathUpdate;Process_FPS_BROWSER_APP_PROFILE_STRING;Process_FPS_BROWSER_USER_PROFILE_STRING;Process_LOGONSERVER;Process_TEMP;Process_USERNAME;Process_SystemRoot;Process_VSSKUEDITION;Process_MSMPI_BIN;Process_OneDrive;Process_LOCALAPPDATA;Process_CommonProgramFiles;Process_R_HOME;Process_ProgramData;Process_VS_Perf_Session_GCHeapCount;Process_VSAPPIDDIR;Process_HOMEPATH;Process_COMPUTERNAME;Process_MONO_ANDROID_PATH;Process_ALLUSERSPROFILE;Process_CommonProgramW6432;Process_OneDriveCommercial;Process_ThreadedWaitDialogDpiContext;Process_GCExpConfigUsedInSession;Process_SignInWithHomeTenantOnly;Process_SESSIONNAME;Process_DriverData;Process_HOMEDRIVE;Process_MSMPI_BENCHMARKS;Process_SystemDrive;Process_NUMBER_OF_PROCESSORS;Process_OS;Process_XAMARIN_ANDROID_REGKEY;Process_ProgramFiles;Process_ComSpec;Process_VSAPPIDNAME;Process_USERDOMAIN_ROAMINGPROFILE;Process_PATHEXT;Process_VSLANG;Process_PSModulePath;Process_APPDATA;Process_USERDOMAIN;Process_PROCESSOR_LEVEL;Process_POSH_THEMES_PATH;Process_VisualStudioVersion;Process_VisualStudioEdition;Process_ServiceHubLogSessionKey;Process_CommonProgramFiles(x86);Process_PUBLIC;Process_ForceIdentityAuthenticationType;Process_MSBuildLoadMicrosoftTargetsReadOnly;Machine_PROCESSOR_REVISION;Machine_ComSpec;Machine_TEMP;Machine_PROCESSOR_LEVEL;Machine_windir;Machine_POWERSHELL_DISTRIBUTION_CHANNEL;Machine_MSMPI_BIN;Machine_DriverData;Machine_PATHEXT;Machine_Path;Machine_NUMBER_OF_PROCESSORS;Machine_PSModulePath;Machine_TMP;Machine_ChocolateyInstall;Machine_MSMPI_BENCHMARKS;Machine_PROCESSOR_ARCHITECTURE;Machine_USERNAME;Machine_PROCESSOR_IDENTIFIER;Machine_OS";
            User = "Surface1";
            IsInCI=false;
            
{ var v=new VersionReleased();
v.Name = "FutureRelease" ;
v.ISODateTime=DateTime.ParseExact("99990416","yyyyMMdd",null); { 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "efd1aa541ad0117edfbb55eda5afc92179debb13";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230521","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "e458336035ec3f6c8b2096de21ab6a2a2e4377aa";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230519","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "d4d900aedf110891ff6fd974a4230f3e64fa8fbf";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230519","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "497f016ee38ed84ad46a224af224dcc079b212a3";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230518","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "752c2603c69d068d6f3cc533dea4febb3ffcc490";
rd.Subject = "Merge pull request #60 from ignatandrei/40-httpsgithubcomltrzesniewskirazorblade";
rd.ReleaseDate = DateTime.ParseExact("20230516","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "85c7e47e29fa4aac1f8bc2451ba3f89938a35e6b";
rd.Subject = "Merge pull request #59 from ignatandrei/35-httpsgithubcomdomn1995dunet";
rd.ReleaseDate = DateTime.ParseExact("20230516","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "74e4bc7adaf110c80d328b957ae6c302969b427c";
rd.Subject = "Merge pull request #58 from ignatandrei/36-httpsgithubcomdistantcamautoctor";
rd.ReleaseDate = DateTime.ParseExact("20230516","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "e6f03e4eaf45c7edbafd6c888f1cc9eee9295ec7";
rd.Subject = "Merge pull request #57 from ignatandrei/37-httpsgithubcomflavienquickconstructor";
rd.ReleaseDate = DateTime.ParseExact("20230515","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "01c13b88674c19f18f3229b3be0643e454fdfb5b";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230515","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "e559ea3ab9adc9cae60dbc942c811eac1dd51e25";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230515","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "201637618cebc273144a4bfb7211a2055c16e80d";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230514","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "a72f3a36c876def2e6c438c3a3c24abe7ba1db0d";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230514","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "c0c54da6c25e12bd886698022ac57f7b11fb8c4c";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230513","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "4c6fbf61eda86bcf2d049462913853ec8c52f1b2";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230513","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "56622984f6dddeca95a0f38f8d0809b96470fdff";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230513","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "f0caab198c26eb1a21e8e341d094222f689cd13f";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230512","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "87d3b7833fc8f99d41683fa3fcee088a7adfed85";
rd.Subject = "Merge pull request #54 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20230511","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "f68ad7b997593ded5e2ba60f65bf0ac295ae36f3";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230510","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "e5e9d0bbb7428cc1950eda609ce618d1394c9a89";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230509","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "95c1e2325ad41b28848a76c98e3b200aaa908a29";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples";
rd.ReleaseDate = DateTime.ParseExact("20230509","yyyyMMdd",null);  
v.AddRelease(rd);
} this.AddVersion(v);}
{ var v=new VersionReleased();
v.Name = "WithVersioning" ;
v.ISODateTime=DateTime.ParseExact("20220402","yyyyMMdd",null); { 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "c5c219a2a3cb26b8d1a46d632cf2dee54653602c";
rd.Subject = "Merge pull request #23 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20211016","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "1b6d21a19e8bc4ec9323d4369b8958d5563b0015";
rd.Subject = "Merge pull request #22 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20211011","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "ignatandrei";
rd.CommitId = "6d47fdc34ac4dc999f2e13fd828cd9f79f3bd41b";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples into main";
rd.ReleaseDate = DateTime.ParseExact("20211005","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "c3778675e5bfbeaa81667ada1fa24a19cf0e1efc";
rd.Subject = "Merge pull request #21 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20211003","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "ea755c59323eefd5c2ff7b2c34220ac5f8dbeaeb";
rd.Subject = "Merge pull request #20 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20210901","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "1ea5abc7b06c3776fd49a991234d63e141a9d1e4";
rd.Subject = "Merge pull request #19 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20210831","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "da6a848014673e96e07f64f00106f050d539142d";
rd.Subject = "Merge pull request #18 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20210821","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "5bccff438ec6604d7eb45925a4714276b0b4bf16";
rd.Subject = "Merge pull request #17 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20210815","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "17aebf5d69a448572ce4fd831f3dd94d56099956";
rd.Subject = "Merge pull request #16 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20210815","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "83e209f09c4511a0e5607925fa730658f0940287";
rd.Subject = "Merge pull request #12 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20210807","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "072cfeeda71acb1d84961291e962b4eab90b16a5";
rd.Subject = "Merge pull request #11 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20210717","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "9172c92711d71da5e5c6aa8929f8ff6a8ac85b9e";
rd.Subject = "Merge pull request #10 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20210625","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "918954a5ac28eb47e041faa90990ffd85a4a3499";
rd.Subject = "Merge pull request #7 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20210621","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "16d62359fc7d807ed652fd9b4218b4aa04734d43";
rd.Subject = "Merge pull request #5 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20210330","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "ignatandrei";
rd.CommitId = "3b082c0aa0f63effd098a2bad184b03447406ec0";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples into main";
rd.ReleaseDate = DateTime.ParseExact("20210308","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "1a998fa87363c6df6b6d4a09dc514503808d380f";
rd.Subject = "Merge pull request #4 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20210308","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "c5fa74a2ad780af288ece391da7a5ac84b06332e";
rd.Subject = "Merge pull request #3 from ignatandrei/imgbot";
rd.ReleaseDate = DateTime.ParseExact("20210307","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "ignatandrei";
rd.CommitId = "1bedfd1d4cb00f666b94fdaecdd7bd7da2e6a435";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples into main";
rd.ReleaseDate = DateTime.ParseExact("20210302","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "ignatandrei";
rd.CommitId = "3223ba3b8e69fc9e72e34735cc135b39881f7b59";
rd.Subject = "Merge branch 'main' of https://github.com/ignatandrei/RSCG_Examples into main";
rd.ReleaseDate = DateTime.ParseExact("20210218","yyyyMMdd",null);  
v.AddRelease(rd);
}{ 
var rd=new ReleaseData();
rd.Author = "Andrei Ignat";
rd.CommitId = "bea2886eb02f17523a34ff16f475654cb4b24ff4";
rd.Subject = "Merge pull request #1 from ignatandrei/whitesource/configure";
rd.ReleaseDate = DateTime.ParseExact("20210217","yyyyMMdd",null);  
v.AddRelease(rd);
} this.AddVersion(v);}
        }
        
    }
        
}

Code and pdf at

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

RSCG – CommunityToolkit.Mvvm

name CommunityToolkit.Mvvm
nuget https://www.nuget.org/packages/CommunityToolkit.Mvvm
link https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/overview
author Microsoft

Shows how to implement INotifyPropertyChanged,ObservableProperty and RelayCommand

Unfortunately , not yet a separate package just for those.

Also, this show that RSCG could generate multiple partial declarations

 

This is how you can use CommunityToolkit.Mvvm .

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="CommunityToolkit.Mvvm" Version="8.2.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 test;

Console.WriteLine("Hello, World!");

MyViewModel myViewModel = new();
myViewModel.Name = "Andrei";
var x=myViewModel.SayHelloCommand;


using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace test;

[INotifyPropertyChanged]
public partial class MyViewModel 
{
    [ObservableProperty]
    private string? name;

    [RelayCommand]
    private void SayHello()
    {
        Console.WriteLine("Hello");
    }
}


 

The code that is generated is

// <auto-generated/>
#pragma warning disable
#nullable enable
namespace test
{
    /// <inheritdoc/>
    partial class MyViewModel : global::System.ComponentModel.INotifyPropertyChanged
    {
        /// <inheritdoc cref = "global::System.ComponentModel.INotifyPropertyChanged.PropertyChanged"/>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        public event global::System.ComponentModel.PropertyChangedEventHandler? PropertyChanged;
        /// <summary>
        /// Raises the <see cref = "PropertyChanged"/> event.
        /// </summary>
        /// <param name = "e">The input <see cref = "global::System.ComponentModel.PropertyChangedEventArgs"/> instance.</param>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs e)
        {
            PropertyChanged?.Invoke(this, e);
        }

        /// <summary>
        /// Raises the <see cref = "PropertyChanged"/> event.
        /// </summary>
        /// <param name = "propertyName">(optional) The name of the property that changed.</param>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected void OnPropertyChanged([global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
        {
            OnPropertyChanged(new global::System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// Compares the current and new values for a given property. If the value has changed, updates
        /// the property with the new value, then raises the <see cref = "PropertyChanged"/> event.
        /// </summary>
        /// <typeparam name = "T">The type of the property that changed.</typeparam>
        /// <param name = "field">The field storing the property's value.</param>
        /// <param name = "newValue">The property's value after the change occurred.</param>
        /// <param name = "propertyName">(optional) The name of the property that changed.</param>
        /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
        /// <remarks>
        /// The <see cref = "PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
        /// </remarks>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected bool SetProperty<T>([global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("newValue")] ref T field, T newValue, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
        {
            if (global::System.Collections.Generic.EqualityComparer<T>.Default.Equals(field, newValue))
            {
                return false;
            }

            field = newValue;
            OnPropertyChanged(propertyName);
            return true;
        }

        /// <summary>
        /// Compares the current and new values for a given property. If the value has changed, updates
        /// the property with the new value, then raises the <see cref = "PropertyChanged"/> event.
        /// See additional notes about this overload in <see cref = "SetProperty{T}(ref T, T, string)"/>.
        /// </summary>
        /// <typeparam name = "T">The type of the property that changed.</typeparam>
        /// <param name = "field">The field storing the property's value.</param>
        /// <param name = "newValue">The property's value after the change occurred.</param>
        /// <param name = "comparer">The <see cref = "global::System.Collections.Generic.IEqualityComparer{T}"/> instance to use to compare the input values.</param>
        /// <param name = "propertyName">(optional) The name of the property that changed.</param>
        /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected bool SetProperty<T>([global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("newValue")] ref T field, T newValue, global::System.Collections.Generic.IEqualityComparer<T> comparer, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
        {
            if (comparer.Equals(field, newValue))
            {
                return false;
            }

            field = newValue;
            OnPropertyChanged(propertyName);
            return true;
        }

        /// <summary>
        /// Compares the current and new values for a given property. If the value has changed, updates
        /// the property with the new value, then raises the <see cref = "PropertyChanged"/> event.
        /// This overload is much less efficient than <see cref = "SetProperty{T}(ref T, T, string)"/> and it
        /// should only be used when the former is not viable (eg. when the target property being
        /// updated does not directly expose a backing field that can be passed by reference).
        /// For performance reasons, it is recommended to use a stateful callback if possible through
        /// the <see cref = "SetProperty{TModel, T}(T, T, TModel, global::System.Action{TModel, T}, string? )"/> whenever possible
        /// instead of this overload, as that will allow the C# compiler to cache the input callback and
        /// reduce the memory allocations. More info on that overload are available in the related XML
        /// docs. This overload is here for completeness and in cases where that is not applicable.
        /// </summary>
        /// <typeparam name = "T">The type of the property that changed.</typeparam>
        /// <param name = "oldValue">The current property value.</param>
        /// <param name = "newValue">The property's value after the change occurred.</param>
        /// <param name = "callback">A callback to invoke to update the property value.</param>
        /// <param name = "propertyName">(optional) The name of the property that changed.</param>
        /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
        /// <remarks>
        /// The <see cref = "PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
        /// </remarks>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected bool SetProperty<T>(T oldValue, T newValue, global::System.Action<T> callback, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
        {
            if (global::System.Collections.Generic.EqualityComparer<T>.Default.Equals(oldValue, newValue))
            {
                return false;
            }

            callback(newValue);
            OnPropertyChanged(propertyName);
            return true;
        }

        /// <summary>
        /// Compares the current and new values for a given property. If the value has changed, updates
        /// the property with the new value, then raises the <see cref = "PropertyChanged"/> event.
        /// See additional notes about this overload in <see cref = "SetProperty{T}(T, T, global::System.Action{T}, string)"/>.
        /// </summary>
        /// <typeparam name = "T">The type of the property that changed.</typeparam>
        /// <param name = "oldValue">The current property value.</param>
        /// <param name = "newValue">The property's value after the change occurred.</param>
        /// <param name = "comparer">The <see cref = "global::System.Collections.Generic.IEqualityComparer{T}"/> instance to use to compare the input values.</param>
        /// <param name = "callback">A callback to invoke to update the property value.</param>
        /// <param name = "propertyName">(optional) The name of the property that changed.</param>
        /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected bool SetProperty<T>(T oldValue, T newValue, global::System.Collections.Generic.IEqualityComparer<T> comparer, global::System.Action<T> callback, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
        {
            if (comparer.Equals(oldValue, newValue))
            {
                return false;
            }

            callback(newValue);
            OnPropertyChanged(propertyName);
            return true;
        }

        /// <summary>
        /// Compares the current and new values for a given nested property. If the value has changed,
        /// updates the property and then raises the <see cref = "PropertyChanged"/> event.
        /// The behavior mirrors that of <see cref = "SetProperty{T}(ref T, T, string)"/>,
        /// with the difference being that this method is used to relay properties from a wrapped model in the
        /// current instance. This type is useful when creating wrapping, bindable objects that operate over
        /// models that lack support for notification (eg. for CRUD operations).
        /// Suppose we have this model (eg. for a database row in a table):
        /// <code>
        /// public class Person
        /// {
        ///     public string Name { get; set; }
        /// }
        /// </code>
        /// We can then use a property to wrap instances of this type into our observable model (which supports
        /// notifications), injecting the notification to the properties of that model, like so:
        /// <code>
        /// [INotifyPropertyChanged]
        /// public partial class BindablePerson
        /// {
        ///     public Model { get; }
        ///
        ///     public BindablePerson(Person model)
        ///     {
        ///         Model = model;
        ///     }
        ///
        ///     public string Name
        ///     {
        ///         get => Model.Name;
        ///         set => Set(Model.Name, value, Model, (model, name) => model.Name = name);
        ///     }
        /// }
        /// </code>
        /// This way we can then use the wrapping object in our application, and all those "proxy" properties will
        /// also raise notifications when changed. Note that this method is not meant to be a replacement for
        /// <see cref = "SetProperty{T}(ref T, T, string)"/>, and it should only be used when relaying properties to a model that
        /// doesn't support notifications, and only if you can't implement notifications to that model directly (eg. by having
        /// it implement <see cref = "global::System.ComponentModel.INotifyPropertyChanged"/>). The syntax relies on passing the target model and a stateless callback
        /// to allow the C# compiler to cache the function, which results in much better performance and no memory usage.
        /// </summary>
        /// <typeparam name = "TModel">The type of model whose property (or field) to set.</typeparam>
        /// <typeparam name = "T">The type of property (or field) to set.</typeparam>
        /// <param name = "oldValue">The current property value.</param>
        /// <param name = "newValue">The property's value after the change occurred.</param>
        /// <param name = "model">The model containing the property being updated.</param>
        /// <param name = "callback">The callback to invoke to set the target property value, if a change has occurred.</param>
        /// <param name = "propertyName">(optional) The name of the property that changed.</param>
        /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
        /// <remarks>
        /// The <see cref = "PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
        /// </remarks>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected bool SetProperty<TModel, T>(T oldValue, T newValue, TModel model, global::System.Action<TModel, T> callback, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
            where TModel : class
        {
            if (global::System.Collections.Generic.EqualityComparer<T>.Default.Equals(oldValue, newValue))
            {
                return false;
            }

            callback(model, newValue);
            OnPropertyChanged(propertyName);
            return true;
        }

        /// <summary>
        /// Compares the current and new values for a given nested property. If the value has changed,
        /// updates the property and then raises the <see cref = "PropertyChanged"/> event.
        /// The behavior mirrors that of <see cref = "SetProperty{T}(ref T, T, string)"/>,
        /// with the difference being that this method is used to relay properties from a wrapped model in the
        /// current instance. See additional notes about this overload in <see cref = "SetProperty{TModel, T}(T, T, TModel, global::System.Action{TModel, T}, string)"/>.
        /// </summary>
        /// <typeparam name = "TModel">The type of model whose property (or field) to set.</typeparam>
        /// <typeparam name = "T">The type of property (or field) to set.</typeparam>
        /// <param name = "oldValue">The current property value.</param>
        /// <param name = "newValue">The property's value after the change occurred.</param>
        /// <param name = "comparer">The <see cref = "global::System.Collections.Generic.IEqualityComparer{T}"/> instance to use to compare the input values.</param>
        /// <param name = "model">The model containing the property being updated.</param>
        /// <param name = "callback">The callback to invoke to set the target property value, if a change has occurred.</param>
        /// <param name = "propertyName">(optional) The name of the property that changed.</param>
        /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected bool SetProperty<TModel, T>(T oldValue, T newValue, global::System.Collections.Generic.IEqualityComparer<T> comparer, TModel model, global::System.Action<TModel, T> callback, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
            where TModel : class
        {
            if (comparer.Equals(oldValue, newValue))
            {
                return false;
            }

            callback(model, newValue);
            OnPropertyChanged(propertyName);
            return true;
        }

        /// <summary>
        /// Compares the current and new values for a given field (which should be the backing field for a property).
        /// If the value has changed, updates the field and then raises the <see cref = "PropertyChanged"/> event.
        /// The behavior mirrors that of <see cref = "SetProperty{T}(ref T, T, string)"/>, with the difference being that
        /// this method will also monitor the new value of the property (a generic <see cref = "global::System.Threading.Tasks.Task"/>) and will also
        /// raise the <see cref = "PropertyChanged"/> again for the target property when it completes.
        /// This can be used to update bindings observing that <see cref = "global::System.Threading.Tasks.Task"/> or any of its properties.
        /// This method and its overload specifically rely on the <see cref = "TaskNotifier"/> type, which needs
        /// to be used in the backing field for the target <see cref = "global::System.Threading.Tasks.Task"/> property. The field doesn't need to be
        /// initialized, as this method will take care of doing that automatically. The <see cref = "TaskNotifier"/>
        /// type also includes an implicit operator, so it can be assigned to any <see cref = "global::System.Threading.Tasks.Task"/> instance directly.
        /// Here is a sample property declaration using this method:
        /// <code>
        /// private TaskNotifier myTask;
        ///
        /// public Task MyTask
        /// {
        ///     get => myTask;
        ///     private set => SetAndNotifyOnCompletion(ref myTask, value);
        /// }
        /// </code>
        /// </summary>
        /// <param name = "taskNotifier">The field notifier to modify.</param>
        /// <param name = "newValue">The property's value after the change occurred.</param>
        /// <param name = "propertyName">(optional) The name of the property that changed.</param>
        /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
        /// <remarks>
        /// The <see cref = "PropertyChanged"/> event is not raised if the current and new value for the target property are
        /// the same. The return value being <see langword="true"/> only indicates that the new value being assigned to
        /// <paramref name = "taskNotifier"/> is different than the previous one, and it does not mean the new
        /// <see cref = "global::System.Threading.Tasks.Task"/> instance passed as argument is in any particular state.
        /// </remarks>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected bool SetPropertyAndNotifyOnCompletion([global::System.Diagnostics.CodeAnalysis.NotNull] ref TaskNotifier? taskNotifier, global::System.Threading.Tasks.Task? newValue, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
        {
            return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new TaskNotifier(), newValue, null, propertyName);
        }

        /// <summary>
        /// Compares the current and new values for a given field (which should be the backing field for a property).
        /// If the value has changed, updates the field and then raises the <see cref = "PropertyChanged"/> event.
        /// This method is just like <see cref = "SetPropertyAndNotifyOnCompletion(ref TaskNotifier, global::System.Threading.Tasks.Task, string)"/>,
        /// with the difference being an extra <see cref = "global::System.Action{T}"/> parameter with a callback being invoked
        /// either immediately, if the new task has already completed or is <see langword="null"/>, or upon completion.
        /// </summary>
        /// <param name = "taskNotifier">The field notifier to modify.</param>
        /// <param name = "newValue">The property's value after the change occurred.</param>
        /// <param name = "callback">A callback to invoke to update the property value.</param>
        /// <param name = "propertyName">(optional) The name of the property that changed.</param>
        /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
        /// <remarks>
        /// The <see cref = "PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
        /// </remarks>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected bool SetPropertyAndNotifyOnCompletion([global::System.Diagnostics.CodeAnalysis.NotNull] ref TaskNotifier? taskNotifier, global::System.Threading.Tasks.Task? newValue, global::System.Action<global::System.Threading.Tasks.Task?> callback, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
        {
            return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new TaskNotifier(), newValue, callback, propertyName);
        }

        /// <summary>
        /// Compares the current and new values for a given field (which should be the backing field for a property).
        /// If the value has changed, updates the field and then raises the <see cref = "PropertyChanged"/> event.
        /// The behavior mirrors that of <see cref = "SetProperty{T}(ref T, T, string)"/>, with the difference being that
        /// this method will also monitor the new value of the property (a generic <see cref = "global::System.Threading.Tasks.Task"/>) and will also
        /// raise the <see cref = "PropertyChanged"/> again for the target property when it completes.
        /// This can be used to update bindings observing that <see cref = "global::System.Threading.Tasks.Task"/> or any of its properties.
        /// This method and its overload specifically rely on the <see cref = "TaskNotifier{T}"/> type, which needs
        /// to be used in the backing field for the target <see cref = "global::System.Threading.Tasks.Task"/> property. The field doesn't need to be
        /// initialized, as this method will take care of doing that automatically. The <see cref = "TaskNotifier{T}"/>
        /// type also includes an implicit operator, so it can be assigned to any <see cref = "global::System.Threading.Tasks.Task"/> instance directly.
        /// Here is a sample property declaration using this method:
        /// <code>
        /// private TaskNotifier&lt;int&gt; myTask;
        ///
        /// public Task&lt;int&gt; MyTask
        /// {
        ///     get => myTask;
        ///     private set => SetAndNotifyOnCompletion(ref myTask, value);
        /// }
        /// </code>
        /// </summary>
        /// <typeparam name = "T">The type of result for the <see cref = "global::System.Threading.Tasks.Task{TResult}"/> to set and monitor.</typeparam>
        /// <param name = "taskNotifier">The field notifier to modify.</param>
        /// <param name = "newValue">The property's value after the change occurred.</param>
        /// <param name = "propertyName">(optional) The name of the property that changed.</param>
        /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
        /// <remarks>
        /// The <see cref = "PropertyChanged"/> event is not raised if the current and new value for the target property are
        /// the same. The return value being <see langword="true"/> only indicates that the new value being assigned to
        /// <paramref name = "taskNotifier"/> is different than the previous one, and it does not mean the new
        /// <see cref = "global::System.Threading.Tasks.Task{TResult}"/> instance passed as argument is in any particular state.
        /// </remarks>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected bool SetPropertyAndNotifyOnCompletion<T>([global::System.Diagnostics.CodeAnalysis.NotNull] ref TaskNotifier<T>? taskNotifier, global::System.Threading.Tasks.Task<T>? newValue, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
        {
            return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new TaskNotifier<T>(), newValue, null, propertyName);
        }

        /// <summary>
        /// Compares the current and new values for a given field (which should be the backing field for a property).
        /// If the value has changed, updates the field and then raises the <see cref = "PropertyChanged"/> event.
        /// This method is just like <see cref = "SetPropertyAndNotifyOnCompletion{T}(ref TaskNotifier{T}, global::System.Threading.Tasks.Task{T}, string)"/>,
        /// with the difference being an extra <see cref = "global::System.Action{T}"/> parameter with a callback being invoked
        /// either immediately, if the new task has already completed or is <see langword="null"/>, or upon completion.
        /// </summary>
        /// <typeparam name = "T">The type of result for the <see cref = "global::System.Threading.Tasks.Task{TResult}"/> to set and monitor.</typeparam>
        /// <param name = "taskNotifier">The field notifier to modify.</param>
        /// <param name = "newValue">The property's value after the change occurred.</param>
        /// <param name = "callback">A callback to invoke to update the property value.</param>
        /// <param name = "propertyName">(optional) The name of the property that changed.</param>
        /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
        /// <remarks>
        /// The <see cref = "PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
        /// </remarks>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected bool SetPropertyAndNotifyOnCompletion<T>([global::System.Diagnostics.CodeAnalysis.NotNull] ref TaskNotifier<T>? taskNotifier, global::System.Threading.Tasks.Task<T>? newValue, global::System.Action<global::System.Threading.Tasks.Task<T>?> callback, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
        {
            return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new TaskNotifier<T>(), newValue, callback, propertyName);
        }

        /// <summary>
        /// Implements the notification logic for the related methods.
        /// </summary>
        /// <typeparam name = "TTask">The type of <see cref = "global::System.Threading.Tasks.Task"/> to set and monitor.</typeparam>
        /// <param name = "taskNotifier">The field notifier.</param>
        /// <param name = "newValue">The property's value after the change occurred.</param>
        /// <param name = "callback">(optional) A callback to invoke to update the property value.</param>
        /// <param name = "propertyName">(optional) The name of the property that changed.</param>
        /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        private bool SetPropertyAndNotifyOnCompletion<TTask>(ITaskNotifier<TTask> taskNotifier, TTask? newValue, global::System.Action<TTask?>? callback, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
            where TTask : global::System.Threading.Tasks.Task
        {
            if (ReferenceEquals(taskNotifier.Task, newValue))
            {
                return false;
            }

            bool isAlreadyCompletedOrNull = newValue?.IsCompleted ?? true;
            taskNotifier.Task = newValue;
            OnPropertyChanged(propertyName);
            if (isAlreadyCompletedOrNull)
            {
                if (callback != null)
                {
                    callback(newValue);
                }

                return true;
            }

            async void MonitorTask()
            {
                await global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__TaskExtensions.GetAwaitableWithoutEndValidation(newValue!);
                if (ReferenceEquals(taskNotifier.Task, newValue))
                {
                    OnPropertyChanged(propertyName);
                }

                if (callback != null)
                {
                    callback(newValue);
                }
            }

            MonitorTask();
            return true;
        }

        /// <summary>
        /// An interface for task notifiers of a specified type.
        /// </summary>
        /// <typeparam name = "TTask">The type of value to store.</typeparam>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        private interface ITaskNotifier<TTask>
            where TTask : global::System.Threading.Tasks.Task
        {
            /// <summary>
            /// Gets or sets the wrapped <typeparamref name = "TTask"/> value.
            /// </summary>
            TTask? Task { get; set; }
        }

        /// <summary>
        /// A wrapping class that can hold a <see cref = "global::System.Threading.Tasks.Task"/> value.
        /// </summary>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected sealed class TaskNotifier : ITaskNotifier<global::System.Threading.Tasks.Task>
        {
            /// <summary>
            /// Initializes a new instance of the <see cref = "TaskNotifier"/> class.
            /// </summary>
            internal TaskNotifier()
            {
            }

            private global::System.Threading.Tasks.Task? task;
            /// <inheritdoc/>
            global::System.Threading.Tasks.Task? ITaskNotifier<global::System.Threading.Tasks.Task>.Task { get => this.task; set => this.task = value; }

            /// <summary>
            /// Unwraps the <see cref = "global::System.Threading.Tasks.Task"/> value stored in the current instance.
            /// </summary>
            /// <param name = "notifier">The input <see cref = "TaskNotifier{TTask}"/> instance.</param>
            public static implicit operator global::System.Threading.Tasks.Task? (TaskNotifier? notifier)
            {
                return notifier?.task;
            }
        }

        /// <summary>
        /// A wrapping class that can hold a <see cref = "global::System.Threading.Tasks.Task{T}"/> value.
        /// </summary>
        /// <typeparam name = "T">The type of value for the wrapped <see cref = "global::System.Threading.Tasks.Task{T}"/> instance.</typeparam>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected sealed class TaskNotifier<T> : ITaskNotifier<global::System.Threading.Tasks.Task<T>>
        {
            /// <summary>
            /// Initializes a new instance of the <see cref = "TaskNotifier{TTask}"/> class.
            /// </summary>
            internal TaskNotifier()
            {
            }

            private global::System.Threading.Tasks.Task<T>? task;
            /// <inheritdoc/>
            global::System.Threading.Tasks.Task<T>? ITaskNotifier<global::System.Threading.Tasks.Task<T>>.Task { get => this.task; set => this.task = value; }

            /// <summary>
            /// Unwraps the <see cref = "global::System.Threading.Tasks.Task{T}"/> value stored in the current instance.
            /// </summary>
            /// <param name = "notifier">The input <see cref = "TaskNotifier{TTask}"/> instance.</param>
            public static implicit operator global::System.Threading.Tasks.Task<T>? (TaskNotifier<T>? notifier)
            {
                return notifier?.task;
            }
        }
    }
}
// <auto-generated/>
#pragma warning disable
#nullable enable
namespace test
{
    /// <inheritdoc/>
    partial class MyViewModel
    {
        /// <inheritdoc cref="name"/>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        public string? Name
        {
            get => name;
            set
            {
                if (!global::System.Collections.Generic.EqualityComparer<string?>.Default.Equals(name, value))
                {
                    OnNameChanging(value);
                    OnNameChanging(default, value);
                    name = value;
                    OnNameChanged(value);
                    OnNameChanged(default, value);
                    OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Name);
                }
            }
        }

        /// <summary>Executes the logic for when <see cref="Name"/> is changing.</summary>
        /// <param name="value">The new property value being set.</param>
        /// <remarks>This method is invoked right before the value of <see cref="Name"/> is changed.</remarks>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")]
        partial void OnNameChanging(string? value);
        /// <summary>Executes the logic for when <see cref="Name"/> is changing.</summary>
        /// <param name="oldValue">The previous property value that is being replaced.</param>
        /// <param name="newValue">The new property value being set.</param>
        /// <remarks>This method is invoked right before the value of <see cref="Name"/> is changed.</remarks>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")]
        partial void OnNameChanging(string? oldValue, string? newValue);
        /// <summary>Executes the logic for when <see cref="Name"/> just changed.</summary>
        /// <param name="value">The new property value that was set.</param>
        /// <remarks>This method is invoked right after the value of <see cref="Name"/> is changed.</remarks>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")]
        partial void OnNameChanged(string? value);
        /// <summary>Executes the logic for when <see cref="Name"/> just changed.</summary>
        /// <param name="oldValue">The previous property value that was replaced.</param>
        /// <param name="newValue">The new property value that was set.</param>
        /// <remarks>This method is invoked right after the value of <see cref="Name"/> is changed.</remarks>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")]
        partial void OnNameChanged(string? oldValue, string? newValue);
    }
}
// <auto-generated/>
#pragma warning disable
#nullable enable
namespace CommunityToolkit.Mvvm.ComponentModel.__Internals
{
    /// <summary>
    /// A helper type providing cached, reusable <see cref="global::System.ComponentModel.PropertyChangedEventArgs"/> instances
    /// for all properties generated with <see cref="global::CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute"/>.
    /// </summary>
    [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCode]
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
    [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
    [global::System.Obsolete("This type is not intended to be used directly by user code")]
    internal static class __KnownINotifyPropertyChangedArgs
    {
        /// <summary>The cached <see cref="global::System.ComponentModel.PropertyChangedEventArgs"/> instance for all "Name" generated properties.</summary>
        [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
        [global::System.Obsolete("This field is not intended to be referenced directly by user code")]
        public static readonly global::System.ComponentModel.PropertyChangedEventArgs Name = new global::System.ComponentModel.PropertyChangedEventArgs("Name");
    }
}
// <auto-generated/>
#pragma warning disable
#nullable enable
namespace test
{
    /// <inheritdoc/>
    partial class MyViewModel
    {
        /// <summary>The backing field for <see cref="SayHelloCommand"/>.</summary>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "8.2.0.0")]
        private global::CommunityToolkit.Mvvm.Input.RelayCommand? sayHelloCommand;
        /// <summary>Gets an <see cref="global::CommunityToolkit.Mvvm.Input.IRelayCommand"/> instance wrapping <see cref="SayHello"/>.</summary>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "8.2.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        public global::CommunityToolkit.Mvvm.Input.IRelayCommand SayHelloCommand => sayHelloCommand ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(SayHello));
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/CommunityToolkit.Mvvm

RSCG – RSCG_Static

 
 

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

Generate interfaces and classes from static classes

 

This is how you can use RSCG_Static .

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="RSCG_Static" Version="2023.5.19.2037" />
	</ItemGroup>

</Project>


The code that you will use is


using RSCG_StaticDemo;
//for DI, register
//ISystem_DateTime  with transient for new clsSystem_DateTime()
Console.WriteLine("Hello World!");
ISystem_DateTime dateStatic = recSystem_DateTime.MakeNew();//static
ISystem_DateTime dateVar = new clsSystem_DateTime(); //variable = real 

Console.WriteLine(dateStatic.Now.Second);
Console.WriteLine(dateVar.Now.Second);
await Task.Delay(10 * 1000);
Console.WriteLine(dateStatic.Now.Second);
Console.WriteLine(dateVar.Now.Second);



namespace RSCG_StaticDemo;

public partial class StaticToInterface
{
    public Type GenerateInterfaceFromDate() => typeof(DateTime);
}


 

The code that is generated is


#nullable enable
 namespace RSCG_StaticDemo {
      public interface ISystem_DateTime {
          public System.DateTime Now  {get;}
          public System.DateTime Today  {get;}
          public System.DateTime UtcNow  {get;}
      }// interface
//now the partial class
      public record recSystem_DateTime (System.DateTime Now,System.DateTime Today,System.DateTime UtcNow) : ISystem_DateTime
      { 
            public static recSystem_DateTime MakeNew() {
            return new recSystem_DateTime(System.DateTime.Now,System.DateTime.Today,System.DateTime.UtcNow);
            } //end makenew
      } //end record
      public class clsSystem_DateTime : ISystem_DateTime 
      { 
            public System.DateTime Now  {get { return System.DateTime.Now; } }
            public System.DateTime Today  {get { return System.DateTime.Today; } }
            public System.DateTime UtcNow  {get { return System.DateTime.UtcNow; } }
       } //end class
 } // namespace
#nullable disable

Code and pdf at

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

File to CSharp Const

I was getting tired how many times I am reading from embedded resources – and finding it over and over the same code. So – what if I will transform the file into C# text ? Seems a work for Roslyn Source Code Generator – and the new one

IIncrementalGenerator

https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md  , not the old ISourceGenerator https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview

So the code reads from additional files defined in .csproj

<ItemGroup>
<AdditionalFiles Include=”Second.gen.txt” />
<AdditionalFiles Include=”first.gen.txt” />
<AdditionalFiles Include=”test\Afirst.gen.txt” />
< /ItemGroup>

and then transforms in a const:

string x= MyAdditionalFiles.Second_gen_txt;

Now, how difficult is the code?

The first version was

public void Initialize(IncrementalGeneratorInitializationContext initContext)
{
IncrementalValuesProvider<AdditionalText> textFiles = initContext
    .AdditionalTextsProvider
    .Where(file => file.Path.Contains(".gen."));

// read their contents and save their name
IncrementalValuesProvider<(string name, string content)> namesAndContents = textFiles
    .Select((text, cancellationToken) => (name: Path.GetFileName(text.Path), content: text.GetText(cancellationToken)!.ToString()));
            
// generate a class that contains their values as const strings
initContext.RegisterSourceOutput(namesAndContents, (spc, nameAndContent) =>
{
                
    string nameString=nameAndContent.name
        .Replace(".","_")
        .Replace("-", "_")
        ;
        
    string[] lines = nameAndContent.content
        .Split('\n', '\r')
        .Where(it => it.Trim().Length > 0)
        .Select(it => it.Replace("\\", "\\\\"))
        .Select(it => it.Replace("\"", "\\\""))
        .Select(it => "\"" + it + "\\r\\n" + "\"")
        .ToArray();
    string content = string.Join("\r\n+", lines);
    string hint = $"MyAdditionalFiles.{nameAndContent.name}";
    var str = $@"
public static partial class MyAdditionalFiles
{{
//https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/raw-string                
public const string {nameString} =  {content};
}}";
    spc.AddSource(hint,str );
});
}

But it have performed poorly ( 3 minutes build for a file with 2MB ) . I was thinking that splitting into lines and replacing quotes and / was the faulty

So raw strings, https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/raw-string , to the help – the second version is

//v1
//string[] lines = nameAndContent.content
//    .Split('\n', '\r')
//    .Where(it => it.Trim().Length > 0)
//    .Select(it => it.Replace("\\","\\\\"))
//    .Select(it=>it.Replace("\"","\\\""))
//    .Select(it => "\"" + it +"\\r\\n"+ "\"")
//    .ToArray();
//string content = string.Join("\r\n+",lines);
//v2 => .net 7
string content=nameAndContent.content;       
//too much, but otherwise start counting quotes
var quotes = new string('"', 10);
content = quotes+"\r\n"+ content+"\r\n"+ quotes;

 

Additional details to be mentioned:

1.The Release with GitHUb Actions should also to be mentioned –the deployment to NUGET is a easy way now

2. Debugging is relative easy https://github.com/JoanComasFdz/dotnet-how-to-debug-source-generator-vs2022

3. It is a day of work to get rid of embedded files!

4. Does not work with binary files ( error with AdditionalFiles )

You can find the package at https://github.com/ignatandrei/RSCG_Utils .

TILT-Server Timing in Browser-part 19

For see fast what is happening with your server, you can use the Server Timing API.

One of the implementations for .NET is https://github.com/tpeczek/Lib.AspNetCore.ServerTiming .

Not a big deal – implemented separately with AOPMethodsCommon – but I think it is a good idea to use it.

The implementation was straightforward

builder.Services.AddScoped<ServerTiming>();
var app = builder.Build();
app.UseServerTiming();

And then use DI to get the instance of the class.

With this occasion , look how is different calling same method twice

vs

If you do not realize, there are 50 ms vs 1 ms . That difference is from caching

private async Task<TILT_Note_Table[]?> privateLatestTILTs(string urlPart, int numberTILTS)
{
    if (cache.TryGetValue<TILT_Note_Table[]>(urlPart, out var result))
    {
        return result;
    }
//code
}

Nice, right ? See the timing right in your browser.

Tools used

https://github.com/tpeczek/Lib.AspNetCore.ServerTiming

https://github.com/ignatandrei/AOP_With_Roslyn/

DIForFunctions–improving generating of a constructor for a existing class with Roslyn

It is not so difficult to generate a new constructor for a existing class with Roslyn – however , there are some difficulties:

1. The original class must be declared “partial” – you cannot declare the second as partial if the first was not

2.  The new class must be in the same namespace ( or without namespace) as the original

3. The constructor must have the types declared with  full names  / or get the usings from the existing class ( or , maybe , put the developer to use global usings: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10

For 1 the dotnet compiler shows an error  – but you can add yours too:

private static bool HasPartial(ClassDeclarationSyntax cds)
         {
             if (cds.Modifiers.Count < 1)
                 return false;
             foreach(var modif in cds.Modifiers)
             {
                 if (modif.Text == “partial”)
                     return true;
             }
             return false;
         }

if (!HasPartial(cds))
                 {
                     var dd = new DiagnosticDescriptor(DiagnosticId, Title, “Please add partial to ” + cds.Identifier.Text, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: “Please add partial to ” + cds.Identifier.Text);
                     Location? loc = cds.GetLocation();
                     var dg = Diagnostic.Create(dd, loc);
                     context.ReportDiagnostic(dg);
                     return;
                 }

For 2 is somehow more difficult

private static Tuple<string,string> NameAndNameSpace(ClassDeclarationSyntax c)
         {
             var nameClass = c.Identifier.Text;
             var p = c.Parent;
             var namespaceClass = “”;
             while (true)
             {
                 if (p is BaseNamespaceDeclarationSyntax bnsds)
                 {
                     namespaceClass = bnsds.Name.ToFullString();
                     break;
                 }
                 p = p?.Parent;
                 if (p == null)
                     break;
             }
             return Tuple.Create(nameClass, namespaceClass);
         }

And verify if the namespace is null – if it is, do NOT generate namespace declaration

For 3 it is more complicated

private static string? FullTypeVar(SemanticModel? sem,TypeSyntax? ts)
         {
             if (sem == null)
                 return null;
             if (ts is null)
                 return null;
             var typeInfo = sem.GetTypeInfo(ts);
             if (typeInfo.Type is null)
                 return null;

            var theType = ((INamedTypeSymbol)typeInfo.Type);

            var typeField = theType?.ToDisplayString();
             return typeField;
         }

And that it is!

DIForFunctions – Improving constructor–part 5

I have received a suggestion : what if we just put into constructor what we need , and everything else ( such as ILogger ) are into fields ?

The Roslyn Source Code Generator will generate a constructor that calls the this constructor  and will assign fields needed.

Let’s give an example : We wrote

public partial class TestDIFunctionAdvWithConstructor2Args
    {
        [RSCG_FunctionsWithDI_Base.FromServices]
        private TestDI1 NewTestDI1;

       public TestDI2 NewTestDI2 { get; set; }

       public readonly TestDI3 myTestDI3;

       private TestDIFunctionAdvWithConstructor2Args(TestDI3 test, TestDI2 a)
        {
            myTestDI3 = test;
            NewTestDI2 = a;
        }

   }

and the generator will generate a new constructor with the required  field

public partial class TestDIFunctionAdvWithConstructor2Args
{
public TestDIFunctionAdvWithConstructor2Args  
(TestDI3 test, TestDI2 a, TestDI1 _NewTestDI1) : this (test,a)
{
this.NewTestDI1 = _NewTestDI1;
}//end constructor

}//class

The code is non trivial  – to find if a constructor exists, take his fields, generate new constructor with all fields.

But , as anything in IT , it is doable .

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.