1

What I am trying to do (working with Roslyn/Microsoft.CodeAnalysis)

I'm trying to find all symbols of interfaces which are inheriting from a specific interface, in the solution as well as referenced assemblies. My goal is to do that as efficient and clean as possible.

At the time of doing that, I have the following things at hand:

  • The roslyn Solution.
  • The symbol of the interface, which I want to find derived interfaces from.

Approaches

Not working

  • 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) => This is also just returning classes (as the method name already states)
  • SymbolFinder.FindReferencesAsync(interfaceSymbol, solution) => This is just returning references in the current solution, but not in referenced assemblies.

Working

  • As the mentioned attempts were not leading to useful results, my last resort was the manual brute force approach where I am basically gathering all the IAssemblySymbols, iterating over all types and checking for the interface (done recursively, with a SymbolVisitor).

So why am I looking for another solution, anyway?

  • I expect a built-in solution to be better in terms of performance as there may already be something cached, or as it also may use other data structures etc., as this is the case behing the scenes in the SymbolFinder
  • Besides of that: less complex, more stable, ...

My question

  • Are there are any simpler and potentially faster solutions for this (similar to what the SymbolFinder already provides)?
Alex
  • 569
  • 5
  • 21

1 Answers1

1

As there are no proposed improvements so far, here is my initial working approach - just for the sake of completeness:

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);
        }
    }
}
Alex
  • 569
  • 5
  • 21