Найдите символы производных интерфейсов в решении и связанных сборках

Что я пытаюсь сделать (работая с Roslyn/Microsoft.CodeAnalysis)

Я пытаюсь найти все символы интерфейсов, которые наследуются от определенного интерфейса, в решении, а также в сборках, на которые есть ссылки. Моя цель — сделать это максимально эффективно и чисто.

В то время, когда я это делаю, у меня есть следующие вещи:

  • Рослин Solution.
  • Символ интерфейса, из которого я хочу найти производные интерфейсы.

подходы

Не работает

  • At first I was trying to use the SymbolFinder, but the following approaches did not work:
    • SymbolFinder.FindImplementationsAsync(interfaceSymbol, solution) => This approach does not work, as it's only returning classes, but no interfaces.
    • SymbolFinder.FindDerivedClassesAsync(interfaceSymbol, solution) => Это также просто возвращает классы (как уже указано в названии метода)
    • SymbolFinder.FindReferencesAsync(interfaceSymbol, solution) => Это просто возвращает ссылки в текущем решении, но не в сборках, на которые ссылаются.

Работающий

  • Поскольку упомянутые попытки не привели к полезным результатам, моим последним средством был подход ручного перебора, когда я в основном собираю все IAssemblySymbols, перебираю все типы и проверяю интерфейс (выполняется рекурсивно, с SymbolVisitor).

Так почему же я все равно ищу другое решение?

  • Я ожидаю, что встроенное решение будет лучше с точки зрения производительности, так как может быть что-то уже кэшировано, или поскольку оно также может использовать другие структуры данных и т. д., как это имеет место в сценах в SymbolFinder
  • Кроме того: менее сложный, более стабильный, ...

Мой вопрос

  • Существуют ли какие-либо более простые и потенциально более быстрые решения для этого (аналогичные тому, что уже предоставляет SymbolFinder)?

person Alex    schedule 09.04.2020    source источник


Ответы (1)


Поскольку до сих пор нет предложенных улучшений, вот мой первоначальный рабочий подход - просто для полноты картины:

private static ConcurrentBag<INamedTypeSymbol> GetImplementingSymbols(Project project)
{
    var compilation = project.GetCompilationAsync().Result;
    var typeToLookFor = compilation.GetTypeByMetadataName(typeof(IAnyInterface).FullName);

    var assemblySymbols =
        project.MetadataReferences
            .Select(compilation.GetAssemblyOrModuleSymbol)
            .OfType<IAssemblySymbol>()
            .ToList();

    assemblySymbols.Add(compilation.Assembly);

    var foundSymbols = new ConcurrentBag<INamedTypeSymbol>();

    Parallel.ForEach(assemblySymbols, symbol =>
    {
        var getAllSymbolsVisitor = new GetAllSymbolsVisitor(typeToLookFor, foundSymbols);
        getAllSymbolsVisitor.Visit(symbol.GlobalNamespace);
    });

    return foundSymbols;
}

private class GetAllSymbolsVisitor : SymbolVisitor
{
    private readonly ConcurrentBag<INamedTypeSymbol> _symbols;
    private INamedTypeSymbol _type;

    public GetAllSymbolsVisitor(INamedTypeSymbol type, ConcurrentBag<INamedTypeSymbol> symbols)
    {
        _symbols = symbols;
        _type = type;
    }

    public override void VisitNamespace(INamespaceSymbol symbol)
    {
        foreach (var namespaceOrTypeSymbol in symbol.GetMembers())
        {
            namespaceOrTypeSymbol.Accept(this);
        }
    }

    public override void VisitNamedType(INamedTypeSymbol symbol)
    {
        if (symbol.Interfaces.Any(interfaceType => SymbolEqualityComparer.Default.Equals(_type, interfaceType)))
        {
            _symbols.Add(symbol);
        }
    }
}
person Alex    schedule 03.05.2020