【问题标题】:Finding a method's declaring type查找方法的声明类型
【发布时间】:2012-02-17 20:00:02
【问题描述】:

给定一个 MethodDeclarationSyntax 对象,如何找出该方法的声明类型?

我的实际问题是我需要弄清楚引用的方法是否实现了接口方法。

例如,给定下面的代码,如果我有 Dispose() 方法的 MethodDeclarationSyntax,如何断定它是 IDisposable.Dispose() 的实现?

using System;
abstract class InterfaceImplementation : IDisposable
{
    public abstract void Dispose();
}

我尝试获取方法的声明类型(并检查类型)但没有成功(父属性返回 InterfaceImplementation 类)。

我也尝试过获取该方法的语义符号:

var methodSymbol = (MethodSymbol) semanticModel.GetDeclaredSymbol(methodDeclaration);

但找不到任何可以帮助我的东西。

想法?

【问题讨论】:

    标签: c# .net roslyn


    【解决方案1】:

    获得方法符号后,您可以询问给定方法是否在给定类型内实现接口方法。代码相当简单:

    MethodSymbol method = ...;
    TypeSymbol type = method.ContainingType;
    MethodSymbol disposeMethod = (MethodSymbol)c.GetSpecialType(SpecialType.System_IDisposable).GetMembers("Dispose").Single();
    bool isDisposeMethod = method.Equals(type.FindImplementationForInterfaceMember(disposeMethod));
    

    请务必注意,这假定包含 Dispose 方法的类型是声明它实现 IDisposable 的类型。在 C# 中,方法可以实现仅在派生类型上声明的接口方法。更具体地说,如果您在上面的代码中省略了“:IDisposable”,并且具有 IDisposable 的派生类型 InterfaceImplementation,则该 Dispose() 方法仍然可以实现它。

    【讨论】:

    • 我会在这里使用== 运算符而不是Equals(),因为FindImplementationForInterfaceMember() 可以返回null。或者至少反过来写Equals()
    • @svick:交换 Equals 排序的要点。我使用 Equals 并非偶然,这是我们在 Roslyn 团队中养成的一个重要习惯:只要您只使用特定于语言的类型,使用 == 就可以正常工作。如果您有两个 IMethodSymbol,则必须使用 Equals,因为在这种情况下 == 不会重载。
    • @Jason 恐怕这对我没有帮助,因为它假设我知道我需要检查哪些方法(在您的代码中,您获取对 Dispose() 方法符号的引用并与之进行比较)事实并非如此。当然,我可以递归地检查基类/接口(直到我到达对象),但我希望 MethodSymbol 类可以直接为我提供这些信息。
    • A MethodSymbol 不提供此功能,但正如凯文指出的那样,它不能。即使该接口未在类型的基类型链中声明,方法也可以实现接口。唯一可以回答的问题是“给定这组类,这个方法 Foo() 是否在它们中的任何一个中实现了接口方法”。 Roslyn 不可能知道世界上所有的类,所以 MethodSymbol 不能直接提供它而不被提供一堆额外的信息。
    • 您在原始问题中没有提到的一件事是,一旦您知道给定方法实现了接口,您打算做什么。如果你不介意我问,你在这里的最终目标是什么?也许更好地了解您的场景可以帮助我们在这里制作更好的 API。
    【解决方案2】:

    语法类型(如MethodDeclarationSyntax在语法级别上操作。在这个级别,不知道方法Dispose 是否实现IDisposable。那是因为你还不知道IDisposable 有什么方法。更何况你甚至不知道IDisposable是否存在,是类还是接口,全名是什么。 (是System.IDisposable?还是MyNamespace.IDisposable?)

    如您所料,要获得这样的信息,您需要达到语义级别。

    我没有找到直接从方法到接口的任何方法,除非它是显式接口实现(编辑:那是因为它并不总是可能的,请参阅 Kevin 的评论)。但是你可以从一个类型中得到一些具体接口方法的实现。

    所以,如果你想知道某个MethodSymbol 实现了IDisposable.Dispose(),你可以这样做:

    SyntaxTree unit = SyntaxTree.ParseCompilationUnit(code);
    
    MethodDeclarationSyntax method = …;
    
    var compilation = Compilation.Create("test")
        .AddReferences(new AssemblyFileReference(typeof(object).Assembly.Location))
        .AddSyntaxTrees(unit);
    
    SemanticModel model = compilation.GetSemanticModel(unit);
    
    MethodSymbol methodSymbol = (MethodSymbol)model.GetDeclaredSymbol(method);
    
    var typeSymbol = methodSymbol.ContainingType;
    
    var idisposableDisposeSymbol = model.BindExpression(
        0, Syntax.ParseExpression("System.IDisposable.Dispose()")).Symbol;
    
    var implementation = typeSymbol.FindImplementationForInterfaceMember(
        idisposableDisposeSymbol);
    
    bool methodImplementsDispose = methodSymbol == implementation;
    

    【讨论】:

    • 从方法上不能做到这一点的原因是,有时候你是分不出来的。如果您有class Base { public void Dispose() } class Derived : Base, IDisposable { },那么“Dispose”是实现如果您有 Derived 的实例,但如果您有 Base 的实例则不是...
    • 嗯,我什至没有意识到这是可能的,有趣。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多