Roslyn Analyzer - 查找接口方法调用的当前具体实现

Roslyn Analyzer - Finding the current concrete implementation of interface's method call

我正在尝试编写一个 Roslyn 分析器来检测来自接口特定实现的方法调用,但我在检测实现类型方面遇到了问题。

当我使用一个类型化的变量作为具体实现时,它正确地识别了方法,但当我使用一个类型化的变量作为接口时,它却不正确。主要是想弄清楚如何使用 Roslyn 代码分析 Apis

获取接口的底层实现类型

这是要分析的示例代码。它正确检测到第二个 Operate 方法调用但不是第一个,即使两者都使用 Operable

中的具体实现
using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            IOperable ioperable = new Operable();
            ioperable.Operate();

            Operable operable = new Operable();
            operable.Operate();
        }
    }

    public interface IOperable
    {
        void Operate();
    }

    public class Operable : IOperable
    {
        public void Operate()
        {

        }
    }
}

这是我当前的来源:

public override void Initialize(AnalysisContext context)
{
    context.RegisterSyntaxNodeAction(AnalyzeInvocation, SyntaxKind.InvocationExpression);
}

private static void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
{
    var invocation = (InvocationExpressionSyntax)context.Node;

    var memberAccess = invocation.Expression as MemberAccessExpressionSyntax;
    if (memberAccess == null || !memberAccess.IsKind(SyntaxKind.SimpleMemberAccessExpression))
    {
        return;
    }

    var ident = memberAccess.Name.Identifier;
    if (ident.ToString().ToLower() != "operate")
    {
        return;
    }

    var calledFromType = context.SemanticModel.GetTypeInfo(memberAccess.Expression).Type;
    if (calledFromType.MetadataName != "Operable")
    {
        return;
    }
    context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation()));
}

我会让你失望,但 Roslyn 是一个静态分析器,默认情况下它不可能检索类型、调用等的动态信息。

为了实现这一目标,您需要在 roslyn 下实施自定义动态分析,该分析将调查所有变量赋值、参数传递等,以确定有关对象(类型、方法等)的所有信息,它可以当前时间在代码中。

在下面的例子中要清楚:

public interface IOperable
{
    void Operate();
}

public class Operable : IOperable
{
   ...
}

public class OperableOther : IOperable
{
   ...
}

...

IOperable ioperable = new Operable();
ioperable.Operate(); // at this time will invoke Operable.Operate();

ioperable = new OperableOther();
ioperable.Operate(); // at this time will OperableOther.Operate();

Roslyn 可以检索关于变量 ioperable 的编译时信息,它是一个 IOperable,但不能给出它将 Operable 从第一次分配和 OperableOther 从第二。但是如果你会累积所有变量修改等,你可以自己做。