2016-06-13 11 views
1
public void SomeMethod(string sampleString) 
{ var helloworld = sampleString; } 

Kann festgestellt werden, ob ein bestimmtes Symbol eine lokale Variable, ein Klassenfeld oder ein Methodenparameter ist? z.B. Wenn ich eine FindSymbolAtPosition für den sampleString aufruft, kann ich dann sagen, dass das sampleString-Symbol der Parameter oder die Variable einer Methode ist?Verstehen/Symboltyp bekommen?

EDIT: die Anforderung ist, dass es von Codierung Zeit, für mein statisches Code-Analyse-Tool, das den Aufbau im mit roslyn

+0

Kodierung-Zeit, für meine statische Code-Analyse-Tool, das im Bau mit Roslyn. –

Antwort

3

Sie können es nicht, weil über Eigenschaften, erhalten direkt in var helloworld = sampleString;, die Aussage keinen Kontext hat, dass, wenn sampleString ein Parameter ist oder nicht. Aber man kann es aus dem Kontext des Verfahrens erhalten wie folgt aus:

static bool IsParameter(IdentifierNameSyntax name) 
{ 
    SyntaxNode node = name; 
    while (node != null && !(node is MethodDeclarationSyntax)) 
    { 
     node = node.Parent; 
    } 

    var method = node as MethodDeclarationSyntax; 
    if (method != null) 
    { 
     return method 
      .ParameterList 
      .Parameters 
      .Any(p => p.Identifier.Text.Equals(name.Identifier.Text)); 
    } 

    return false;    
} 

Mit .Parent des Verfahrens Kontext der Variablen zu erhalten, und prüfen Sie, ob ein Parameter in .ParameterList die Kennung übereinstimmt.

UPDATE-Code zu beweisen, dass es funktioniert:

SyntaxTree tree = CSharpSyntaxTree.ParseText(
    @"using System; 
    using System.Collections; 
    using System.Linq; 
    using System.Text; 

    namespace HelloWorld 
    { 
     class Program 
     { 
      static void Main(string i) 
      { 
       var j = ""1""; 
       var k = i + j; 
      } 
     } 
    }"); 

var root = (CompilationUnitSyntax)tree.GetRoot(); 
var ns = root.Members[0] as NamespaceDeclarationSyntax; 
var cls = ns.Members[0] as ClassDeclarationSyntax; 
var method = cls.Members[0] as MethodDeclarationSyntax; 
var statement = method.Body.Statements[1] as LocalDeclarationStatementSyntax; 
var variable = statement.Declaration.Variables[0]; 
var binary = variable.Initializer.Value as BinaryExpressionSyntax; 
var vari = binary.Left as IdentifierNameSyntax; 
var varj = binary.Right as IdentifierNameSyntax; 
Console.WriteLine(IsParameter(vari)); //True 
Console.WriteLine(IsParameter(varj)); //False 

EDIT Base auf @JeroenVannevel ‚s Kommentar, können wir SemanticModel.GetSymbolInfo verwenden.

var compilation = CSharpCompilation.Create("test", new[] { tree });     
var semanticModel = compilation.GetSemanticModel(tree, true); 
var symboli = semanticModel.GetSymbolInfo(vari); 
var symbolj = semanticModel.GetSymbolInfo(varj); 
//check symboli.Symbol.OriginalDefinition.Kind == SymbolKind.Parameter 
+0

ja, aber dies ist eine Art von String-Vergleich, ich bin mehr interessiert, wenn es eine api oder eine bessere Möglichkeit, dies zu tun, als den Vergleich nach Identifier-Namen. trotzdem danke. –

+0

@KimKangIn Wie kann der Mensch sagen, ob ein Bezeichner ein Parameter ist oder nicht? Indem man es mit der Parameterliste vergleicht, was bedeutet, dass der Bezeichner-Name verglichen wird, ist der richtige (und wahrscheinlich der einzige) Weg. –

+1

Wenn Sie nicht mit einem Bezeichner vergleichen möchten (was ich nicht akzeptieren sollte), können Sie einfach das Symbol des Parameters abrufen und es mit dem Symbol der lokalen Variablen vergleichen. Verwenden Sie 'GetDeclaredSymbol' und' GetSymbolInfo'. –

-3

Sie unterscheiden können, arbeiten müssen, ob Sie mit einem Klassenfeld vs a tun haben Methodenparameter oder eine lokale Variable mit dem Schlüsselwort this. Wenn Sie über eine Klassenfeldzeichenfolge namens sampleString verfügen und lieber auf das Klassenfeld verweisen möchten als auf den Parameter local/method, dann verweisen Sie mit diesem.sampleString darauf. Sie würden auf den Parameter local_variable/method als sampleString ohne das Schlüsselwort (this) verweisen. In Bezug auf lokale Variablen und Methodenparameter können Sie innerhalb derselben Methode keine lokale Variable und denselben Methodenparameter mit demselben Namen verwenden. Mit dem obigen Code beziehen Sie sich auf den Parameter sampleString.

+0

Das ist nicht, was OP für – MickyD

+0

fragt Ich meine indirekt ist es, aber ich denke, ich habe es nicht klar genug erklärt.Wenn Sie FindSymbolAtPosition für sampleString innerhalb der Methode verwenden, verwendet er sie für den Parameter und nicht für das Klassenfeld. Wenn er FindSymbolAtPosition für diesen.sampleString verwendet, wird er ihn im Klassenfeld verwenden. Dies setzt voraus, dass es eine lokale Variable und ein Klassenfeld mit dem Namen sampleString gibt. Parameter werden ähnlich wie lokale Variablen behandelt, weshalb Sie einen Parameter und eine lokale Variable nicht innerhalb derselben Methode mit demselben Namen deklarieren können. –

+0

In seinem aktuellen Code-Snippet bezieht er sich zweifellos auf den Parameter. Wenn Sie eine Parameter/lokale Variable innerhalb einer Methode haben, die denselben Namen wie ein Klassenfeld hat und Sie sie aufrufen, ohne das Schlüsselwort (this) zu verwenden, rufen Sie immer den Parameter/local variable auf, nicht das Klassenfeld. –

-2

Sie können den folgenden Code verwenden, um die Feldinformationen für Klasse und Parameter abzurufen. Beachten Sie jedoch, dass die Erkennung des Methodenfelds nicht verfügbar ist. Dieser Code verwendet Reflektion, um die Assemblyinformationen abzufragen und die Ergebnisse aufzuzählen und zu vergleichen.

static class Program 
{ 
    static void Main(string[] args) 
    { 

     SomeMethod("Hello, World!!!"); 

     Type testType = typeof(Program); 
     FieldInfo[] fieldInfo = testType.GetFields(); 
     MethodInfo methodInfo = testType.GetMethod("SomeMethod"); 

     Console.WriteLine("Parameter type:{0}", TypeOfField(fieldInfo, methodInfo, "sampleString")); 
     Console.WriteLine("Parameter type:{0}", TypeOfField(fieldInfo, methodInfo, "classField")); 
     Console.WriteLine("Parameter type:{0}", TypeOfField(fieldInfo, methodInfo, "helloWorld")); 
     Console.WriteLine("Parameter type:{0}", TypeOfField(fieldInfo, methodInfo, "nonexistentVariable")); 

    } 

    public static string classField = "Hello, World!!!"; 

    public static void SomeMethod(string sampleString) 
    { 
     string helloWorld = sampleString; 
    } 

    public static string TypeOfField(FieldInfo[] fieldInfo, MethodInfo methodInfo, string fieldName) 
    { 
     if (IsClassField(fieldInfo, fieldName)) 
     { 
      return "Class Field"; 
     } 
     else if (IsParameter(methodInfo, fieldName)) 
     { 
      return "Parameter"; 
     } 
     else 
     { 
      return "Cannot determine"; 
     } 
    } 

    private static bool IsClassField(FieldInfo[] fieldInfo, string classFieldName) 
    { 
     bool isClassField = false; 

     foreach (var item in fieldInfo) 
     { 
      if (item.Name == classFieldName) 
      { 
       isClassField = true; 
       break; 
      } 
     } 
     return isClassField; 
    } 

    private static bool IsParameter(MethodInfo methodInfo, string parameterName) 
    { 
     bool isParameter = false; 
     ParameterInfo[] paramInfo = methodInfo.GetParameters(); 
     foreach (var item in paramInfo) 
     { 
      if (item.Name == parameterName) 
      { 
       isParameter = true; 
       break; 
      } 
     } 
     return isParameter; 
    } 
} 
+1

Reflexion ist völlig getrennt von Roslyn. Dies beantwortet die Frage nicht. –

+0

@JeroenVannevel Das war nicht Teil der ursprünglichen Frage. –

+1

Es wurde immer Roslyn getaggt und die im OP erwähnte Methode ist auch Teil von Roslyn. –

0

Seine falsche Kennungen von Syntax zu vergleichen oder von Text, vorstellen, wenn die IdentifierNameSyntax der Name MemberAccessExpressionSyntax ist und hat die gleiche identifier als parameter, dann würden Sie falsch schließen, dass es sich um eine parameter ist, obwohl es a member. Sie sollten mit der SemanticModel arbeiten, um festzustellen, was ein SymbolKind ist. Sie können SemanticModel.GetDeclaredSymbol für Deklarationen und SemanticModel.GetSymbolInfo().Symbol für Symbolverwendungen verwenden. Sobald Sie die ISymbol haben, ist es einfach, seine Art zu bestimmen. Denken Sie daran, dass verschiedene Symbole ihre eigenen "Unterarten" haben, zum Beispiel hat TypeKind Eigenschaft, die bestimmt, ob der Typ Class, Struct, Interface, Array usw. ist, also sollten Sie sie überprüfen.