Roslyn C#:如何获取方法访问的所有字段和属性(及其所属 class)

Roslyn C#: How to get all fields and properties (and their belonging class) accessed by a method

我希望为我的方法计算访问外部数据指标。此外,我想知道我的方法从哪些对象访问字段和属性以确定 feature envy code smells 的存在。

对于这个例子:

public class DoctorService
{
    private List<Doctor> _doctors;
    public Doctor FindAvailableDoctor(DateRange timeSpan)
    {
        foreach (Doctor d in _doctors)
        {
            foreach(DateRange holiday in d.HolidayDates)
            {
                d.Test = null;
                if (!holiday.OverlapsWith(timeSpan)) return d;
            }
        }
        return null;
    }
}

特别是 FindAvailableDoctor 方法,我想构建一个访问字段列表,其中包含 Doctor class 的 HolidayDates 和 Test 属性。我还想知道这两个属性都属于 Doctor class(由 class 名称及其命名空间标识)。

我编写了以下代码来完成此操作:

var accessedFields = member.DescendantNodes().OfType<MemberAccessExpressionSyntax>();
foreach (var field in accessedFields)
{
    var symbol = semanticModel.GetSymbolInfo(field.Expression).Symbol;
    switch(symbol)
    {
        case ILocalSymbol local:
            fields.Add(new CaDETMember { Name = local.Type + "|" + local.Name });
            break;
        case IPropertySymbol prop:
            fields.Add(new CaDETMember { Name = prop.ContainingSymbol + "|" + field.ToString() });             
            break;
        case IParameterSymbol param:
            fields.Add(new CaDETMember { Name = param.Type + "|" + field.ToString() });
            break;
    };
}

并开始摆弄 Symbol API 的字段。但是,这个解决方案既不干净,而且我很确定会错过一些重要代码的边缘情况。

有什么更好的方法来提取方法访问的字段和 属性 名称,以便我也可以知道访问的字段和属性属于哪个 class?

编辑:根据 Jason 的回答,我采用了以下解决方案:

var accessedFields = semanticModel.GetOperation(member).Descendants().OfType<IMemberReferenceOperation>();
foreach (var field in accessedFields)
{
    fields.Add(new CaDETMember {Name = field.Member.ToDisplayString()});
}
return fields;

你的方法实际上是一个非常好的方法,所以不要为此感到难过。还有第二种方法,即使用 IOperation API。如果您调用 SemanticModel.GetOperation() ,它会为您提供一棵 IOperations 树,您可以遍历它表示在代码中的各个点执行的语义操作。特别是有一个 IMemberReferenceOperation 将指向被引用的成员。所以像:

var memberReferences = semanticModel.GetOperation(methodSyntax).Descendants().OfType<IMemberReferenceOperation>()

会让你看看那些,然后从那里得到符号。本地访问还有其他操作。