Найти символы производных интерфейсов в решениях и ссылочных сборках - PullRequest
0 голосов
/ 09 апреля 2020

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

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

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

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

Подходы

Не работает

  • Сначала я пытался использовать SymbolFinder, но следующие подходы не работали:
    • SymbolFinder.FindImplementationsAsync(interfaceSymbol, solution) => Этот подход не работает, так как он только возвращает классы, но не интерфейсы.
    • SymbolFinder.FindDerivedClassesAsync(interfaceSymbol, solution) => Это также просто возвращает классы (как уже указано в имени метода)
    • SymbolFinder.FindReferencesAsync(interfaceSymbol, solution) => Это просто возвращает ссылки в текущем решении, но не в ссылках сборки.

Работа

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

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

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

Мой вопрос

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

1 Ответ

0 голосов
/ 03 мая 2020

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

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