Category: .NET Core

NetPackageAnalyzer- idea –part 1

I wanted to have a tool that can analyze a C# solution with all the dependencies ( project relations, packages, commits)

It generates a graph of the dependencies for

  • the packages of your solution ( and see when the major version of a package differs between projects)

  • the projects of your solution.
  • the project dependencies of each project.

Along with the graph, it also generates

  • a report for the commits of the solution per years.

  • a report for outdated/deprecated packages per solution.
  • building blocks for the solution – you can start from here to understand the solution

If you want to see how it looks like, you can see how he analyses himself at Analyzer

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

Pattern: AbstractFactory

Description

Abstract Factory is a creational design pattern that lets you produce families of related objects without specifying their concrete classes.

Example in .NET :

AbstractFactory


using Microsoft.Data.SqlClient;
using System.Data.Common;

namespace AbstractFactory;
internal class AbstractFactoryDemo
{
    public static void Demo()
    {
        //create DbConnection factory by using the instance of SqlConnection
        DbConnection connection = new SqlConnection();
        //create DbCommand instance by using the instance of SqlConnection
        DbCommand command = connection.CreateCommand();
        //really, the DBCommand is a SqlCommand
        SqlCommand? sqlCommand = command as SqlCommand;
        //check if the DbCommand is a SqlCommand
        Console.WriteLine($"DbCommand is SqlCommand: {sqlCommand != null}");
    }
}

Learn More

Source Code for Microsoft implementation of AbstractFactory

SourceCode DbConnection

Learn More

Wikipedia

}

Homework

Imagine you want to produce loggers. You have a logger that logs to a file and a logger that logs to a console and a Nothing Logger – a logger that does nothing. Implement an abstract factory that will allow you to create a logger factory that will create a logger that logs to a file or to a console or nothing.

Pattern: Flyweight

Description

Flyweight pattern is used to reduce the memory and resource usage for complex models containing a large number of similar objects.

Example in .NET :

Flyweight

using System.Text;

namespace Flyweight;
internal class FlyweightDemo
{
    public static void Demo()
    {
        var str = "Andrei Ignat";
        var str2 = string.Intern(str);
        var str3 = new StringBuilder("Andrei").Append(" Ignat").ToString();
        Console.WriteLine($"str == str2: Value {str==str2} Reference {Object.ReferenceEquals(str,str2)}");
        Console.WriteLine($"str == str3: Value {str==str3} Reference {Object.ReferenceEquals(str,str3)}");

    }
}

Learn More

Source Code for Microsoft implementation of Flyweight

SourceCode String.Intern

Learn More

Wikipedia

}

Homework

Make an exchange rate system. The symbol and names of the currency are the same for all the currencies. The exchange rate is different for each currency. Implement a flyweight that will allow you to create a currency with a symbol and a name and to get the exchange rate for the currency.

Pattern: Observer

Description

Observer pattern is a behavioral design pattern that defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Example in .NET :

Observer


using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace Observer;

/// <summary>
/// INotifyPropertyChanged is an interface that provides a mechanism for the object to notify clients that a property value has changed.
/// </summary>
public class Person: INotifyPropertyChanged
{
    private string name=string.Empty;
    public string Name
    {
        get => name;
        set
        {
            if (name == value) return;
            name = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
internal class ObserverDemo
{
    public static void Demo()
    {
        Person person = new ();
        //subscribe to the event to observe the changes
        person.PropertyChanged += (sender, args) =>
        {
            var p = sender as Person;
            Console.WriteLine($"Property {args.PropertyName} changed to {p?.Name}");
        };
        person.Name = "Andrei Ignat" ;
    }
}

Learn More

Source Code for Microsoft implementation of Observer

SourceCode INotifyPropertyChanged

Learn More

Wikipedia

}

Homework

Imagine you have a logger that logs to a file and to a console. Implement an observable logger that will allow you to subscribe to the logger and to be notified when the logger logs a message.

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

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.