Roslyn 提供了两个主要的 API 来分析代码:语法 API(Syntax API)和语义 API(Semantic API)。其中,语法 API 用于分析代码的结构,而语义 API 则专注于分析代码的语义和含义。本文将重点介绍与语义分析相关的 API 使用方法。
Compilation
代表编译器视角下的一个完整项目,包含:
C#// 创建编译单元
var compilation = CSharpCompilation.Create("HelloWorld")
.AddReferences(MetadataReference.CreateFromFile(
typeof(string).Assembly.Location))
.AddSyntaxTrees(syntaxTree);
SemanticModel
提供了类似IDE智能提示的功能:
C#// 获取语义模型
SemanticModel model = compilation.GetSemanticModel(syntaxTree);
C#using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
namespace AppRoslyn
{
internal class Program
{
static void Main(string[] args)
{
// 示例程序
const string code = @"
using System;
namespace Demo
{
class Program
{
static void Main()
{
Console.WriteLine(""Hello"");
}
}
}";
// 解析语法树
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
var root = tree.GetCompilationUnitRoot();
// 创建编译单元和语义模型
var compilation = CSharpCompilation.Create("Demo")
.AddReferences(MetadataReference.CreateFromFile(typeof(string).Assembly.Location))
.AddSyntaxTrees(tree);
var model = compilation.GetSemanticModel(tree);
// 获取using System的符号信息
var firstUsing = root.Usings[0];
var systemName = firstUsing.Name;
var symbolInfo = model.GetSymbolInfo(systemName);
// 分析System命名空间
var systemSymbol = (INamespaceSymbol)symbolInfo.Symbol;
foreach (var ns in systemSymbol.GetNamespaceMembers())
{
Console.WriteLine(ns); // 输出子命名空间
}
Console.ReadKey();
}
}
}
C#// 查找字符串字面量
var stringLiteral = root.DescendantNodes()
.OfType<LiteralExpressionSyntax>()
.First();
// 获取类型信息
TypeInfo typeInfo = model.GetTypeInfo(stringLiteral);
var stringType = (INamedTypeSymbol)typeInfo.Type;
// 查询string类型的所有公共方法
var publicMethods = from method in stringType.GetMembers().OfType<IMethodSymbol>()
where method.DeclaredAccessibility == Accessibility.Public
select method.Name;
// 输出方法名
foreach(var name in publicMethods.Distinct())
{
Console.WriteLine(name);
}
C#using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
namespace AppRoslyn
{
internal class Program
{
static void Main(string[] args)
{
const string code = @"
using System;
namespace Demo
{
class Program
{
static void Main()
{
Console.WriteLine(""Hello"");
}
}
}";
// 解析语法树
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
var root = tree.GetCompilationUnitRoot();
// 创建编译单元和语义模型
var compilation = CSharpCompilation.Create("Demo")
.AddReferences(MetadataReference.CreateFromFile(typeof(string).Assembly.Location))
.AddSyntaxTrees(tree);
var model = compilation.GetSemanticModel(tree);
// 专门查找 Main 方法
FindMainMethod(root, model);
Console.ReadLine();
}
static void FindMainMethod(CompilationUnitSyntax root, SemanticModel model)
{
// 查找所有方法声明
var methodDeclarations = root.DescendantNodes()
.OfType<MethodDeclarationSyntax>();
// 筛选 Main 方法
var mainMethods = methodDeclarations
.Where(method =>
method.Identifier.Text == "Main" &&
method.Modifiers.Any(SyntaxKind.StaticKeyword)
);
foreach (var mainMethod in mainMethods)
{
var methodSymbol = model.GetDeclaredSymbol(mainMethod);
if (methodSymbol != null)
{
Console.WriteLine("找到 Main 方法:");
Console.WriteLine($"方法名: {mainMethod.Identifier}");
Console.WriteLine($"所在类型: {methodSymbol.ContainingType.Name}");
Console.WriteLine($"返回类型: {methodSymbol.ReturnType}");
Console.WriteLine("方法体:");
Console.WriteLine(mainMethod.Body?.ToFullString() ?? "空方法体");
}
}
if (!mainMethods.Any())
{
Console.WriteLine("未找到 Main 方法");
}
}
}
}
Roslyn的语义分析API提供了强大的代码分析能力:
通过合理使用这些API,我们可以构建代码分析工具、重构工具等高级开发辅助工具。
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!