2012-02-17 32 views
5

Dado un objeto MethodDeclarationSyntax, ¿cómo puedo averiguar el tipo de declaración del método?Encontrar el tipo de declaración de un método

Mi problema real es que necesito averiguar si el método al que se hace referencia está implementando un método de interfaz o no.

Por ejemplo, dado el código de abajo, si tengo una MethodDeclarationSyntax para la Desechar ( método), ¿cómo se puede concluir que es la aplicación de la IDisposable.Dispose()?

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

He tratado de obtener el tipo de declarar el método (y comprobar el tipo) sin éxito (propiedad Parent me devuelve la clase InterfaceImplementation).

También he tratado de agarrar el símbolo semántica para el método:

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

, pero no pudo detectar nada de lo que me podía ayudar.

Ideas?

Respuesta

7

Una vez que tenga el símbolo método, se puede preguntar si un determinado método está implementando una interfaz método dentro de un tipo dado. El código es bastante simple:

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

Es importante tener en cuenta esto supone que el tipo que contiene el método Dispose es el tipo que indica que implementa IDisposable. En C#, es posible que un método implemente un método de interfaz que solo se establece en un tipo derivado. Más concretamente, si omitió el ": IDisposable" en su código anterior, y tenía un tipo derivado de InterfaceImplementation que era IDisposable, ese método Dispose() todavía puede implementarlo.

+0

Usaría el operador '==' en lugar de 'Equals()' aquí, porque 'FindImplementationForInterfaceMember()' puede devolver 'null'. O al menos escriba 'Equals()' al revés. – svick

+0

@svick: buen punto para cambiar el orden Equals. Mi uso de Equals no es por accidente, como un hábito importante que hemos desarrollado en el equipo de Roslyn: el uso de == funcionará bien siempre y cuando solo uses los tipos específicos del idioma. Si tenía dos IMethodSymbols, * debe * usar Equals ya que == no está sobrecargado en ese caso. –

+0

@ Jason Tengo miedo de que esto no me ayude, ya que asume que sé qué métodos debo verificar (en tu código, tomas una referencia al símbolo del método Dispose() y lo comparas), que no es el caso. Por supuesto, puedo verificar la clase base/interfaces recursivamente (hasta que llegue al objeto) pero esperaría que la clase MethodSymbol podría proporcionarme esta información directamente. – Vagaus

4

Los tipos de sintaxis (como MethodDeclarationSyntax) operan solo en el nivel sintáctico. En este nivel, no se sabe si el método Dispose implementa IDisposable. Eso es porque aún no sabes qué métodos tiene IDisposable. Además, ni siquiera sabe si existe IDisposable, ya sea una clase o interfaz, o cuál es su nombre completo. (¿Es System.IDisposable? O MyNamespace.IDisposable?)

Para obtener información como esa, debe llegar al nivel semántico, como ya habrá adivinado.

No encontré ninguna forma de pasar directamente de un método a la interfaz, a menos que sea una implementación de interfaz explícita (EDITAR: eso no siempre es posible, vea el comentario de Kevin). Pero puede pasar de un tipo a la implementación de algún método de interfaz específico.

Por lo tanto, si usted quiere saber que un cierto MethodSymbol implementa IDisposable.Dispose(), usted podría hacer algo como:

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; 
+1

La razón por la que no puedes hacer esto con el método es que a veces no puedes decirlo. Si tiene 'clase Base {public void Dispose()} clase Derived: Base, IDisposable {}' luego "Dispose" es la implementación _si tiene una instancia de Derived_, pero no si tiene una instancia de Base ... –

+0

Hmm, no me di cuenta de que eso fuera posible, interesante. – svick

Cuestiones relacionadas