создание исходного кода на основе других классов сборки (генератор исходного кода c#)

#c# #sourcegenerators #csharp-source-generator

Вопрос:

Я хочу создать статический класс, у которого должен быть метод, зависящий от других классов в конкретной ссылочной сборке.

упрощенный пример:

 // 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 классы в Core проекте и создать класс в Persistence проекте,
Проблема в том, что мой Core проект недоступен, и он уже был скомпилирован ранее Persistence . должен ли я использовать отражение или вручную прочитать основные объекты ? или есть лучший способ получить доступ к синтаксическому дереву в Core проекте?

Ответ №1:

Поскольку Core проект уже скомпилирован, мы не можем получить доступ к синтаксическому дереву, но мы можем пройти компиляцию, чтобы получить сборки, на которые имеются ссылки, затем просмотреть эти сборки и найти символы.

 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);
}

 

надеюсь, этот пример поможет другим.