4

我有一个类库项目 First.csproj,其中包含一个文件 ICar.cs:

namespace First
{
    public interface ICar
    {
        string Name { get; set; }
    }
}

我有一个空的类库项目 Second.csproj 和分析器(源生成器)项目 Second.Generator.csproj:

  1. First.csproj - 没有项目引用
  2. Second.csproj - 引用了 First.csproj 和 Second.Generator.csproj
  3. Second.Generator.csproj - 没有项目引用

我想编写 Second.Generator.csproj MySourceGenerator.cs,它采用 Second.csproj,搜索其所有类库项目引用(在本例中为 First.csproj)并实现其所有接口。结果应该是这个生成的代码:

namespace Second
{
    public class Car : First.ICar
    {
        public string Name { get; set; }
    }
}

问题是我无法访问源生成器中的引用项目。我曾尝试使用反射:

namespace Second.Generator
{
    [Generator]
    public class MySourceGenerator : ISourceGenerator
    {
        public void Initialize(GeneratorInitializationContext context)
        {
        }

        public void Execute(GeneratorExecutionContext context)
        {
            var first = context.Compilation.References.First(); //this is First.dll

            var assembly = Assembly.LoadFrom(first.Display);
        }
    }
}

但我无法加载程序集:

无法加载文件或程序集 'file:///...First\bin\Debug\net6.0\ref\First.dll' 或其依赖项之一。不应加载引用程序集以供执行。它们只能在 Reflection-only loader 上下文中加载。

任何帮助将不胜感激。谢谢你。

4

1 回答 1

6

我已经找到了一些装配符号的方法。使用反射不是一个好主意。

namespace Second.Generator
{
    [Generator]
    public class MySourceGenerator : ISourceGenerator
    {
        public void Initialize(GeneratorInitializationContext context)
        {
        }

        public void Execute(GeneratorExecutionContext context)
        {
            var types = context.Compilation.SourceModule.ReferencedAssemblySymbols.SelectMany(a =>
            {
                try
                {
                    var main = a.Identity.Name.Split('.').Aggregate(a.GlobalNamespace, (s, c) => s.GetNamespaceMembers().Single(m => m.Name.Equals(c)));

                    return GetAllTypes(main);
                }
                catch
                {
                    return Enumerable.Empty<ITypeSymbol>();
                }
            });

            var properties = types.Where(t => t.TypeKind == TypeKind.Interface && t.DeclaredAccessibility == Accessibility.Public).Select(t => new
            {
                Interface = t,
                Properties = t.GetMembers()
            });
        }

        private static IEnumerable<ITypeSymbol> GetAllTypes(INamespaceSymbol root)
        {
            foreach (var namespaceOrTypeSymbol in root.GetMembers())
            {
                if (namespaceOrTypeSymbol is INamespaceSymbol @namespace) foreach (var nested in GetAllTypes(@namespace)) yield return nested;

                else if (namespaceOrTypeSymbol is ITypeSymbol type) yield return type;
            }
        }
    }
}
于 2021-08-10T22:04:58.253 回答