基于其他程序集类生成源代码(C# 源代码生成器)

12
我希望生成一个静态类,该类应该有一个与特定引用程序集中的其他类相关的方法。
一个简化的例子:
// Generator.csproj
[Generator]
   public class MyGenerator : ISourceGenerator
   {
      public void Initialize(GeneratorInitializationContext context)
      {
          // Register a factory that can create our custom syntax receiver
          context.RegisterForSyntaxNotifications(() => new MySyntaxReceiver());
      }

      public void Execute(GeneratorExecutionContext context)
      {
          // var syntaxReceiver = (MySyntaxReceiver)context.SyntaxReceiver;
      }
   }

    private class MySyntaxReceiver : ISyntaxReceiver
    {
       ....
    }

// Core.csproj
// namespace Core.Entities
class Entity1 : IAccessControl {}
class Entity2  {}
class Entity3 : IAccessControl {}

// Persistence.csproj => has a reference to Core project and the Generator
// this class should be generated ...
static class GeneratedClass
{
   public static void DoSomethingEntity1()
   public static void DoSomethingEntity3()
}

我想在“核心”项目中查找“实体(Entity)”类,并在“持久化(Persistence)”项目中生成一个类, 问题是我的“核心(Core)”项目无法访问并且已经在“持久化(Persistence)”之前编译。我应该使用反射(reflection)还是手动读取“核心(Core)”实体?或者有更好的方法来访问“核心(Core)”项目中的语法树(SyntaxTree)吗?
1个回答

23

由于 Core 项目已经编译完成,我们无法访问 SyntaxTree,但我们可以通过编译来获取引用的程序集,然后浏览这些程序集并查找 symbols

public void Execute(GeneratorExecutionContext context)
{
// finding Core reference assembly Symbols
 IAssemblySymbol assemblySymbol = 
context.Compilation.SourceModule.ReferencedAssemblySymbols.First(q => q.Name == "Core");

// use assembly symbol to get namespace and type symbols
// all members in namespace Core.Entities
var members = assemblySymbol.GlobalNamespace.
                             GetNamespaceMembers().First(q => q.Name == "Core")                                       
                            .GetNamespaceMembers().First(q => q.Name == "Entities")
                            .GetTypeMembers().ToList();

var targets = new HashSet<INamedTypeSymbol>();

// find classes that implemented IAccessControl
foreach (var member in members.Where(m => m.AllInterfaces.Any(i => i.Name == "IAccessControl")))
{
   targets.Add(member); // Entity1 Entity3
}


// generate source using targets ...
// context.AddSource("GeneratedClass", source);
}

希望这个例子能帮助其他人。


直接通过 Compilation.GlobalNamespace 获取 GlobalNamespace 怎么样? - Martin Han
@MartinHan 我不确定是否有其他方法可以完成同样的事情。那时这个方法对我来说非常完美,如果你能发帖介绍其他更好的实现同样结果的方法那就太好了。 - AliReza Sabouri

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接