2016-06-14 1 views
2

Par exemple:Roslyn: L'analyse de l'objet appelant la méthode

SqlCommand command = new SqlCommand(); 
SqlDataReader datareader = command.ExecuteReader(); 

Le nœud d'invocation est ici command.ExecuteReader(). Comment puis-je, en utilisant roslyn, obtenir le jeton/noeud d'identificateur de variable de command à partir du nœud d'invocation? En supposant que ce nœud d'invocation peut avoir plusieurs autres appels de méthode avant lui, par ex. classA.methodA().methodB().classB.methodC(command.ExecuteReader()) et donc obtenir des identifiants par node.DescendantNodes pourrait ne pas être utile. La solution que je pensais était d'obtenir le SpanStart de ExecuteReader en premier, puis en obtenant le symbole de command en appelant SymbolFinder.FindSymbolAtPosition avec la position de ExecuteReader.SpanStart - 2. Cependant, je ne suis pas sûr si cette solution peut gérer chaque situation. L'application sur laquelle je travaille est un analyseur de code statique.

+3

En fin de ceux-ci sont tout simplement enchaîné invocations/simple membre accède. Si vous obtenez l'invocation la plus interne (ou 'SimpleMemberAccessExpression') vous avez ce que vous voulez. Regardez le parent immédiat au lieu de tous les ancêtres. –

+0

est là comme une meilleure façon au lieu de coder en dur? Par exemple, obtenir le symbole de l'appel de la méthode, puis à partir du symbole, détermine l'instance d'un objet appelant la méthode. par exemple: 'ClassA varA = new ClassA(); varA.methodA() 'obtient le symbole de' methodA() 'et découvre que' varA' l'appelle. –

+0

Que voulez-vous dire par "coder en dur"? Si vous voulez cette invocation, vous devez écrire le code qui obtient l'invocation. –

Répondre

4

Lorsque vous avez un nœud d'invocation, vous pouvez voir si son expression est un accès membre ou non. Si l'invocation est pour une instruction "DoThis()" alors il n'y a pas d'accès membre mais si l'invocation est pour "x.DoThis()" alors est un accès membre depuis "DoThis" est appelé contre le référence "x".

Une fois que vous confirmez que est un accès membre, vous pouvez alors obtenir l'expression de la référence cible - c'est la référence dont le membre est en cours d'accès. Cette expression peut être un simple identifiant de nom (par exemple "command") ou un autre membre (par exemple "x.command") ou une autre invocation (par exemple "GetCommand()") une combinaison de ceux-ci.

Pour illustrer avec le code -

private static void AnalyseInvocation(SyntaxNodeAnalysisContext context) 
{ 
    var invocation = (InvocationExpressionSyntax)context.Node; 
    var memberAccess = invocation.Expression as MemberAccessExpressionSyntax; 
    if ((memberAccess == null) || (memberAccess.Name.Identifier.ValueText != "ExecuteReader")) 
     return; 

    if (memberAccess.Expression is IdentifierNameSyntax) 
    { 
     // The target is a simple identifier, the code being analysed is of the form 
     // "command.ExecuteReader()" and memberAccess.Expression is the "command" 
     // node 
    } 
    else if (memberAccess.Expression is InvocationExpressionSyntax) 
    { 
     // The target is another invocation, the code being analysed is of the form 
     // "GetCommand().ExecuteReader()" and memberAccess.Expression is the 
     // "GetCommand()" node 
    } 
    else if (memberAccess.Expression is MemberAccessExpressionSyntax) 
    { 
     // The target is a member access, the code being analysed is of the form 
     // "x.Command.ExecuteReader()" and memberAccess.Expression is the "x.Command" 
     // node 
    } 
} 
+0

hmm..mais si c'est une longue chaîne d'expression d'invocation, cela fonctionnera-t-il encore? par exemple, 'node.ChildNodes(). OfType (). Single(). Name', à partir du visualiseur de syntaxe dans Visual Studio, l'invocation.express de Single() est' node.ChildNodes(). OfType < MemberAccessExpressionSyntax>(). Single' qui, à partir du visualiseur de syntaxe, est un 'MemberAccessExpressionSyntax' d'où il ne satisfait pas la condition de' "x.Command.ExecuteReader()" ', corrigez-moi si je me trompe. Merci! et désolé pour la réponse très tardive. lol. –

+0

désolé je pense que j'ai mal compris votre code, de sorte que vous êtes en train de vérifier 'invocation.Expression.Expression' droite? alors dans ce cas je pense que vous avez raison sur l'approche –

+0

@KimKangIn oui, je vérifie essentiellement invocation.Expression.Expression - cela va retourner la chaîne complète avant l'appel "ExecuteReader", peu importe la façon dont cette chaîne est simple ou complexe. J'ai légèrement modifié l'exemple de code pour ignorer tout appel qui n'est pas pour "ExecuteReader" de sorte que si vous obtenez ce code dans l'EDI et mettez un point d'arrêt sur la première ligne "if (memberAccess.Expression is .." alors vous verrez en inspectant la référence memberAccess que c'est toujours * tout * avant "ExecuteReader" dans l'invocation. –