Understanding/Getting 符号类型?
Understanding/Getting symbol type?
public void SomeMethod(string sampleString)
{ var helloworld = sampleString; }
是否可以确定特定符号是局部变量、class 字段还是方法的参数?例如如果我在 sampleString 上调用 FindSymbolAtPosition,我是否能够分辨出 sampleString 符号是方法的参数或变量?
编辑:要求是它必须从编码时间开始工作,用于我用roslyn构建的静态代码分析工具
您可以通过关键字 this 来区分您是在处理 class 字段还是方法的参数或局部变量。如果您有一个名为 sampleString 的 class 字段字符串,并且您想要引用 class 字段而不是局部变量/方法参数,那么您可以使用 this.sampleString 来引用它。您可以将局部变量/方法参数称为不带关键字 (this) 的 sampleString。在局部变量和方法参数方面,您不能在同一个方法中拥有同名的局部变量和方法参数。使用上面的代码,您指的是 sampleString 参数。
您可以使用以下代码获取class和参数的字段信息。但是请注意,方法字段的检测不可用。此代码使用反射查询程序集信息并枚举和比较结果。
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;
}
}
不能通过属性直接获取,因为在var helloworld = sampleString;
中,语句没有sampleString
是否为参数的上下文。但是您可以像这样从方法的上下文中获取它:
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;
}
使用.Parent
获取变量的方法上下文,并检查.ParameterList
中的任何参数是否与标识符匹配。
更新 证明其有效的代码:
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
编辑 根据@JeroenVannevel的评论,我们可以使用SemanticModel.GetSymbolInfo
.
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
通过语法或文本比较标识符是错误的,想象一下如果 IdentifierNameSyntax
是 MemberAccessExpressionSyntax
的名称并且与 parameter
具有相同的 identifier
,那么你会错误地认为它是 parameter
,即使它是 member
。您应该使用 SemanticModel
来确定 SymbolKind
是什么。您可以在声明中使用 SemanticModel.GetDeclaredSymbol
,在符号用法中使用 SemanticModel.GetSymbolInfo().Symbol
。一旦你有了 ISymbol
就很容易确定它的种类。请记住,不同的符号有自己的 "sub-kinds",例如 ITypeSymbol
有 TypeKind 属性 来确定类型是否为 Class
、Struct
, Interface
, Array
等,所以你应该检查一下。
public void SomeMethod(string sampleString)
{ var helloworld = sampleString; }
是否可以确定特定符号是局部变量、class 字段还是方法的参数?例如如果我在 sampleString 上调用 FindSymbolAtPosition,我是否能够分辨出 sampleString 符号是方法的参数或变量?
编辑:要求是它必须从编码时间开始工作,用于我用roslyn构建的静态代码分析工具
您可以通过关键字 this 来区分您是在处理 class 字段还是方法的参数或局部变量。如果您有一个名为 sampleString 的 class 字段字符串,并且您想要引用 class 字段而不是局部变量/方法参数,那么您可以使用 this.sampleString 来引用它。您可以将局部变量/方法参数称为不带关键字 (this) 的 sampleString。在局部变量和方法参数方面,您不能在同一个方法中拥有同名的局部变量和方法参数。使用上面的代码,您指的是 sampleString 参数。
您可以使用以下代码获取class和参数的字段信息。但是请注意,方法字段的检测不可用。此代码使用反射查询程序集信息并枚举和比较结果。
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;
}
}
不能通过属性直接获取,因为在var helloworld = sampleString;
中,语句没有sampleString
是否为参数的上下文。但是您可以像这样从方法的上下文中获取它:
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;
}
使用.Parent
获取变量的方法上下文,并检查.ParameterList
中的任何参数是否与标识符匹配。
更新 证明其有效的代码:
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
编辑 根据@JeroenVannevel的评论,我们可以使用SemanticModel.GetSymbolInfo
.
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
通过语法或文本比较标识符是错误的,想象一下如果 IdentifierNameSyntax
是 MemberAccessExpressionSyntax
的名称并且与 parameter
具有相同的 identifier
,那么你会错误地认为它是 parameter
,即使它是 member
。您应该使用 SemanticModel
来确定 SymbolKind
是什么。您可以在声明中使用 SemanticModel.GetDeclaredSymbol
,在符号用法中使用 SemanticModel.GetSymbolInfo().Symbol
。一旦你有了 ISymbol
就很容易确定它的种类。请记住,不同的符号有自己的 "sub-kinds",例如 ITypeSymbol
有 TypeKind 属性 来确定类型是否为 Class
、Struct
, Interface
, Array
等,所以你应该检查一下。