Hand.GenerateCore 0.2.0.6

There is a newer version of this package available.
See the version list below for details.
dotnet add package Hand.GenerateCore --version 0.2.0.6
                    
NuGet\Install-Package Hand.GenerateCore -Version 0.2.0.6
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Hand.GenerateCore" Version="0.2.0.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Hand.GenerateCore" Version="0.2.0.6" />
                    
Directory.Packages.props
<PackageReference Include="Hand.GenerateCore" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Hand.GenerateCore --version 0.2.0.6
                    
#r "nuget: Hand.GenerateCore, 0.2.0.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Hand.GenerateCore@0.2.0.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Hand.GenerateCore&version=0.2.0.6
                    
Install as a Cake Addin
#tool nuget:?package=Hand.GenerateCore&version=0.2.0.6
                    
Install as a Cake Tool

SourceGenerator之partial范式

  • 封装SourceGenerator常用功能
  • partial范式的最佳实践

一、什么是partial范式

  • partial关键字允许将一个类或方法分散到多个文件中
  • 所以partial是代码生成的一个很好的抓手
  • 再配合Attribute特性,可以更准确定位需要生成代码的类或方法
  • 对代码按规则自动补足,减少重复代码编写及其可能导致的失误
  • 笔者称之为SourceGenerator的partial范式
  • 开源项目Hand.GenerateCore用于践行partial范式

二、partial范式的要素

1. 标记定位

  • 通过Attribute特性来标记需要代码补足的位置
  • Attribute的命名最好与调用的SourceGenerator一致
  • 需要生成的类有相应的Attribute也可以增加可读性(有预期该类包含自动生成的代码)
  • partial范式通过官方方法SyntaxValueProvider.ForAttributeWithMetadataName来标记定位

2. 节点过滤

  • ISyntaxFilter是节点过滤接口
  • SyntaxFilter是默认实现,实现按节点类型和是否为partial来过滤
interface ISyntaxFilter
{
    bool Match(SyntaxNode node, CancellationToken cancellation = default);
}
class SyntaxFilter(bool isPartial, params SyntaxKind[] kinds)
    : ISyntaxFilter

3. 转化源对象

  • IGeneratorSource是转化源接口
  • GenerateFileName是生成文件名属性
  • Generate是生成代码方法
public interface IGeneratorSource
{
    string GenerateFileName { get; }
    SyntaxGenerator Generate();
}

4. 转化过滤

  • 对节点预处理
  • 如果不满足生成必要条件返回null会被自动过滤
  • ISyntaxTransform是转化接口
  • PassTransform是默认实现,直接返回官方对象
  • TSource一般实现接口IGeneratorSource
interface IGeneratorTransform<TSource>
{
    TSource? Transform(GeneratorAttributeSyntaxContext context, CancellationToken cancellation);
}
class PassTransform : IGeneratorTransform<GeneratorAttributeSyntaxContext>
{
    public GeneratorAttributeSyntaxContext Transform(GeneratorAttributeSyntaxContext context, CancellationToken cancellation)
        => context;
}

5. 执行生成

  • IGeneratorExecutor是执行接口
  • GeneratorExecutor是默认实现,一般可以执行使用
interface IGeneratorExecutor<TSource>
{
    void Execute(SourceProductionContext context, TSource source);
}
class GeneratorExecutor<TSource> : IGeneratorExecutor<TSource>
    where TSource : IGeneratorSource
{
    public virtual void Execute(SourceProductionContext context, TSource source)
    {
        var cancellation = context.CancellationToken;
        if (cancellation.IsCancellationRequested)
            return;
        var builder = source.Generate();
        var code = builder.Build()
            .WithGenerated()
            .ToFullString();
        context.AddSource(source.GenerateFileName, code);
    }
}

6. 生成器基类ValuesGenerator

  • 通过ValuesGenerator简化代码生成器开发
  • 把业务逻辑都提取到TSource中
  • filter、transform和executor都会很简单
class ValuesGenerator<TSource>(
    string attributeName, 
    ISyntaxFilter filter, 
    ISyntaxTransform<TSource> transform, 
    ISyntaxExecutor<TSource> executor);

三、通过ValuesGenerator实现代码生成器的Case

  • 定义类型HelloGenerator继承ValuesGenerator即可
  • 另外需要实现HelloGeneratorAttribute、HelloTransform和HelloSource

1. HelloGenerator代码非常简单

  • 含义是查找HelloGenerator标记
  • 查找含partial修饰的类
  • 转化为HelloSource
  • 用HelloSource生成代码
public class HelloGenerator()
    : ValuesGenerator<HelloSource>(
    "GenerateCoreTests.Hello.HelloGeneratorAttribute",
    new SyntaxFilter(true, SyntaxKind.ClassDeclaration),
    new HelloTransform(),
    new GeneratorExecutor<HelloSource>())
{
}

2. HelloGeneratorAttribute非常简单

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class HelloGeneratorAttribute : Attribute
{
}

3. HelloTransform非常简单

public class HelloTransform : IGeneratorTransform<HelloSource>
{
    public HelloSource? Transform(GeneratorAttributeSyntaxContext context, CancellationToken cancellation)
    {
        if (context.TargetNode is ClassDeclarationSyntax type && context.TargetSymbol is INamedTypeSymbol symbol)
            return new(type, symbol);
        return null;
    }
}

4. HelloSource是比较纯净的业务逻辑

public class HelloSource(ClassDeclarationSyntax type, INamedTypeSymbol symbol)
    : IGeneratorSource
{
    private readonly ClassDeclarationSyntax _type = type;
    private readonly INamedTypeSymbol _symbol = symbol;
    public string GenerateFileName
        => $"{_symbol.ToDisplayString()}.Hello.g.cs";
    public SyntaxGenerator Generate()
    {
        var builder = SyntaxGenerator.Clone(_type);
        var method = GenerateMethod();
        builder.AddMember(method);
        return builder;
    }
    public static MethodDeclarationSyntax GenerateMethod()
    {
        var name = SyntaxFactory.IdentifierName("name");
        var expression = SyntaxGenerator.Interpolation()
            .Add("Hello: '")
            .Add(name)
            .Add("'")
            .Build();

        return SyntaxGenerator.VoidType.Method("SayHello", SyntaxGenerator.StringType.Parameter(name.Identifier))
            .Public()
            .Static()
            .ToBuilder()
            .Add(SyntaxFactory.IdentifierName("Console").Access("WriteLine").Invocation([expression]))
            .End();
    }
}

5. 测试代码如下

namespace GenerateCoreTests.Hello;

[HelloGenerator]
public partial class HelloTests;

6. 生成代码如下

// <auto-generated/>
namespace GenerateCoreTests.Hello;
partial class HelloTests
{
    public static void SayHello(string name)
    {
        Console.WriteLine($"Hello: '{name}'");
    }
}
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Hand.GenerateCore:

Package Downloads
Hand.GenerateCachedProperty

Package Description

Hand.GenerateProperty

Package Description

Hand.GeneratePoco

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.0.11 6 5/19/2026
0.2.0.10 41 5/18/2026
0.2.0.9 37 5/18/2026
0.2.0.8 47 5/17/2026
0.2.0.7 83 5/6/2026
0.2.0.6 79 5/5/2026
0.2.0.5 86 5/1/2026
0.2.0.4 94 4/28/2026
0.2.0.3 100 4/27/2026
0.2.0.2 169 4/26/2026
0.2.0 215 3/26/2026
0.1.0-alpha 137 3/18/2026