2012-02-17 14 views
5

Dato un oggetto MethodDeclarationSyntax come posso trovare il tipo di dichiarazione del metodo?Individuazione del tipo di dichiarazione di un metodo

Il mio problema reale è che ho bisogno di capire se il metodo di riferimento sta implementando un metodo di interfaccia o meno.

Ad esempio, dato il codice soffietto, se ho un MethodDeclarationSyntax per la Dispose metodo(), come si può concludere che è l'implementazione del IDisposable.Dispose()?

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

Ho cercato di ottenere il tipo dichiarando del metodo (e controllare il tipo) senza successo (proprietà Parent mi restituisce classe InterfaceImplementation).

Ho anche cercato di afferrare il simbolo semantica per il metodo:

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

ma non riuscivo a individuare qualcosa che mi potrebbe aiutare.

Idee?

risposta

7

Una volta che hai il simbolo metodo, si può chiedere se un determinato metodo sta attuando un'interfaccia metodo all'interno di un determinato tipo. Il codice è abbastanza semplice:

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

È importante notare che questo assume il tipo che contiene il metodo Dispose è il tipo che indica implementa IDisposable. In C#, è possibile per un metodo implementare un metodo di interfaccia che è indicato solo su un tipo derivato. Più concretamente, se hai omesso il ": IDisposable" sul tuo codice sopra, e avevi un tipo derivato di InterfaceImplementation che era IDisposable, quel metodo Dispose() può ancora implementarlo.

+0

Vorrei usare l'operatore '==' invece di 'Equals()' qui, perché 'FindImplementationForInterfaceMember()' può restituire 'null'. O almeno scrivi il 'Equals()' al contrario. – svick

+0

@svick: buon punto di scambio degli ordini di Equals. Il mio utilizzo di Equals non è un caso, come un'abitudine importante che abbiamo sviluppato nel team di Roslyn: l'utilizzo di == funzionerà correttamente finché si utilizzano solo i tipi specifici della lingua. Se hai due IMethodSymbols, * devi * usare Equals come == non è sovraccarico in quel caso. –

+0

@Jason Ho paura che questo non mi aiuti poiché presuppone di sapere quali metodi devo controllare (nel codice, prendi un riferimento al simbolo del metodo Dispose() e confrontalo con quello) che non è il caso. Naturalmente posso controllare ricorsivamente la classe base/le interfacce (finché non raggiungo l'oggetto) ma mi aspetto che la classe MethodSymbol possa fornirmi direttamente queste informazioni. – Vagaus

4

I tipi di sintassi (come MethodDeclarationSyntax) operano solo a livello sintattico. A questo livello, non si sa se il metodo Dispose implementa IDisposable. Questo perché non sai ancora quali metodi ha IDisposable. Inoltre, non sai nemmeno se esiste IDisposable, che si tratti di una classe o di un'interfaccia o qual è il suo nome completo. (È System.IDisposable? O MyNamespace.IDisposable?)

Per ottenere informazioni del genere, è necessario arrivare al livello semantico, come avete indovinato.

Non ho trovato alcun modo per passare direttamente da un metodo all'interfaccia, a meno che non si tratti di un'implementazione esplicita dell'interfaccia (EDIT: perché non è sempre possibile, vedere il commento di Kevin). Ma puoi ottenere da un tipo all'implementazione di alcuni specifici metodi di interfaccia.

Quindi, se si vuole scoprire che un certo MethodSymbol implementa IDisposable.Dispose(), si potrebbe fare qualcosa di simile:

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 ragione per cui non si può fare questo metodo è che a volte non si può dire. Se hai classe 'Base {public void Dispose()} classe Derived: Base, IDisposable {}' quindi "Dispose" è l'implementazione _se tu hai un'istanza di Derived_, ma non se hai un'istanza di Base ... –

+0

Hmm, non mi rendevo conto che è anche possibile, interessante. – svick

Problemi correlati