You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在编译时动态生成C#类?寻求IDE可识别的最优方案

最优技术方案:.NET源代码生成器(Source Generator)

核心原理

源代码生成器是.NET 5及以上版本官方提供的编译时扩展能力,它能在项目编译过程中分析现有代码的语法和语义信息,动态生成符合需求的C#源代码文件。这些文件会被直接纳入编译流程,IDE(如Visual Studio、Rider)能实时识别并提供智能提示、代码跳转等功能——完全等同于手写代码的体验,正好解决你提到的Fody注入代码无法被IDE识别的问题。

具体实现步骤

1. 创建源代码生成器项目

新建一个类库项目,目标框架选择netstandard2.0(确保兼容绝大多数.NET版本),并添加以下NuGet包:

Microsoft.CodeAnalysis.CSharp
Microsoft.CodeAnalysis.Analyzers

2. 实现生成器逻辑

编写实现ISourceGenerator接口的生成器类,通过语法接收器收集目标实体类,再根据实体类的元数据生成Provider代码:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Text;

[Generator]
public class EntityProviderGenerator : ISourceGenerator
{
    public void Initialize(GeneratorInitializationContext context)
    {
        // 注册语法接收器,用于筛选目标实体类
        context.RegisterForSyntaxNotifications(() => new EntitySyntaxReceiver());
    }

    public void Execute(GeneratorExecutionContext context)
    {
        var receiver = (EntitySyntaxReceiver)context.SyntaxReceiver!;
        foreach (var classDecl in receiver.TargetEntities)
        {
            // 获取实体类的语义符号(包含命名空间、属性等元数据)
            var classSymbol = context.Compilation.GetSemanticModel(classDecl.SyntaxTree)
                .GetDeclaredSymbol(classDecl) as INamedTypeSymbol;
            if (classSymbol == null) continue;

            // 生成Provider类的源代码
            var source = GenerateProviderCode(classSymbol);
            // 将生成的代码添加到编译流程,文件名后缀为.g.cs便于识别
            context.AddSource($"{classSymbol.Name}Provider.g.cs", SourceText.From(source, Encoding.UTF8));
        }
    }

    private string GenerateProviderCode(INamedTypeSymbol entitySymbol)
    {
        var entityName = entitySymbol.Name;
        var providerName = $"{entityName}Provider";
        var ns = entitySymbol.ContainingNamespace.ToString();

        // 可扩展逻辑:比如遍历实体所有属性生成赋值代码,适配复杂需求
        return $$"""
            namespace {{ns}};

            public class {{providerName}}
            {
                public {{entityName}} GetEntity(object content) => new {{entityName}} { Content = content };
            }
            """;
    }
}

// 语法接收器:筛选需要生成Provider的实体类(这里以类名以Entity结尾为例,也可自定义属性标记)
public class EntitySyntaxReceiver : ISyntaxReceiver
{
    public List<ClassDeclarationSyntax> TargetEntities { get; } = new();

    public void OnVisitSyntaxNode(SyntaxNode node)
    {
        if (node is ClassDeclarationSyntax classDecl && classDecl.Identifier.Text.EndsWith("Entity"))
        {
            TargetEntities.Add(classDecl);
        }
    }
}

3. 在主项目中引用生成器

修改主项目的.csproj文件,添加对生成器项目的引用,注意设置OutputItemType为Analyzer,ReferenceOutputAssembly为false:

<ProjectReference Include="..\EntityProviderGenerator\EntityProviderGenerator.csproj"
                  OutputItemType="Analyzer"
                  ReferenceOutputAssembly="false" />

4. 验证效果

编译主项目后,生成的SomeProvider类会出现在obj/Debug/netX/generated/EntityProviderGenerator目录下,IDE会自动识别该类,支持智能提示和代码跳转;当SomeEntity类修改时,生成器会自动重新生成对应的Provider代码。

方案对比与优势

  • Fody:基于IL注入实现,仅在编译后修改程序集,IDE无法识别注入的代码,不适合需要IDE感知的代码生成场景;
  • T4模板:需要手动触发生成,编译时不会自动同步更新,集成度远低于源代码生成器;
  • 自定义脚本/工具:需要手动执行或配置构建事件,无法实时同步代码变化,维护成本高。

源代码生成器的核心优势:

  • IDE实时感知:生成的代码完全融入IDE的代码分析流程,和手写代码无差别;
  • 编译时同步:实体类修改后自动重新生成Provider,确保代码一致性;
  • 类型安全:基于语义分析生成代码,能精准获取实体类的元数据(属性、方法、命名空间等),避免字符串拼接的错误;
  • 增量生成:仅在目标实体类变化时重新生成对应的代码,编译效率更高。

内容的提问来源于stack exchange,提问作者Snowind

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 07:20:35