右键项目,管理NuGet程序包
输入Microsoft.CodeAnalysis


SyntaxNode中密封类【sealed class】代表了 C# 语言中具体、不可再分的语法结构。




关键标记Token枚举Microsoft.CodeAnalysis.CSharp.SyntaxKind
【仅列举部分标记】
//
// 摘要:
// Represents & token.
AmpersandToken = 8198,
//
// 摘要:
// Represents * token.
AsteriskToken = 8199,
//
// 摘要:
// Represents ( token.
OpenParenToken = 8200,
//
// 摘要:
// Represents ) token.
CloseParenToken = 8201,
//
// 摘要:
// Represents - token.
MinusToken = 8202,
//
// 摘要:
// Represents + token.
PlusToken = 8203,
//
// 摘要:
// Represents = token.
EqualsToken = 8204,
//
// 摘要:
// Represents { token.
OpenBraceToken = 8205,
//
// 摘要:
// Represents } token.
CloseBraceToken = 8206,
//
// 摘要:
// Represents [ token.
OpenBracketToken = 8207,
//
// 摘要:
// Represents ] token.
CloseBracketToken = 8208,
//
// 摘要:
// Represents | token.
BarToken = 8209,
//
// 摘要:
// Represents \ token.
BackslashToken = 8210,
//
// 摘要:
// Represents : token.
ColonToken = 8211,
//
// 摘要:
// Represents ; token.
SemicolonToken = 8212,
//
// 摘要:
// Represents " token.
DoubleQuoteToken = 8213,
//
// 摘要:
// Represents ' token.
SingleQuoteToken = 8214,
//
// 摘要:
// Represents < token.
LessThanToken = 8215,
//
// 摘要:
// Represents , token.
CommaToken = 8216,
//
// 摘要:
// Represents > token.
GreaterThanToken = 8217,
//
// 摘要:
// Represents . token.
DotToken = 8218,
//
// 摘要:
// Represents ? token.
QuestionToken = 8219,
//
// 摘要:
// Represents # token.
HashToken = 8220,
//
// 摘要:
// Represents / token.
SlashToken = 8221,
//
// 摘要:
// Represents .. token.
DotDotToken = 8222,
//
// 摘要:
// Represents /> token.
SlashGreaterThanToken = 8232,
//
// 摘要:
// Represents </ token.
LessThanSlashToken = 8233,
//
// 摘要:
// Represents bool.
BoolKeyword = 8304,
//
// 摘要:
// Represents byte.
ByteKeyword = 8305,
//
// 摘要:
// Represents sbyte.
SByteKeyword = 8306,
//
// 摘要:
// Represents short.
ShortKeyword = 8307,
//
// 摘要:
// Represents ushort.
UShortKeyword = 8308,
//
// 摘要:
// Represents int.
IntKeyword = 8309,
//
// 摘要:
// Represents uint.
UIntKeyword = 8310,
//
// 摘要:
// Represents long.
LongKeyword = 8311,
//
// 摘要:
// Represents ulong.
ULongKeyword = 8312,
//
// 摘要:
// Represents double.
DoubleKeyword = 8313,
//
// 摘要:
// Represents float.
FloatKeyword = 8314,
//
// 摘要:
// Represents decimal.
DecimalKeyword = 8315,
//
// 摘要:
// Represents string.
StringKeyword = 8316,
//
// 摘要:
// Represents char.
CharKeyword = 8317,
//
// 摘要:
// Represents void.
VoidKeyword = 8318,
//
// 摘要:
// Represents object.
ObjectKeyword = 8319,
//
// 摘要:
// Represents typeof.
TypeOfKeyword = 8320,
//
// 摘要:
// Represents sizeof.
SizeOfKeyword = 8321,
//
// 摘要:
// Represents null.
NullKeyword = 8322,
//
// 摘要:
// Represents true.
TrueKeyword = 8323,
//
// 摘要:
// Represents false.
FalseKeyword = 8324,
//
// 摘要:
// Represents if.
IfKeyword = 8325,
//
// 摘要:
// Represents else.
ElseKeyword = 8326,
//
// 摘要:
// Represents while.
WhileKeyword = 8327,
//
// 摘要:
// Represents for.
ForKeyword = 8328,
//
// 摘要:
// Represents foreach.
ForEachKeyword = 8329,
//
// 摘要:
// Represents do.
DoKeyword = 8330,
//
// 摘要:
// Represents switch.
SwitchKeyword = 8331,
//
// 摘要:
// Represents case.
CaseKeyword = 8332,
//
// 摘要:
// Represents default.
DefaultKeyword = 8333,
//
// 摘要:
// Represents try.
TryKeyword = 8334,
//
// 摘要:
// Represents catch.
CatchKeyword = 8335,
//
// 摘要:
// Represents finally.
FinallyKeyword = 8336,
//
// 摘要:
// Represents lock.
LockKeyword = 8337,
//
// 摘要:
// Represents goto.
GotoKeyword = 8338,
//
// 摘要:
// Represents break.
BreakKeyword = 8339,
//
// 摘要:
// Represents continue.
ContinueKeyword = 8340,
//
// 摘要:
// Represents return.
ReturnKeyword = 8341,
//
// 摘要:
// Represents throw.
ThrowKeyword = 8342,
//
// 摘要:
// Represents public.
PublicKeyword = 8343,
//
// 摘要:
// Represents private.
PrivateKeyword = 8344,
//
// 摘要:
// Represents internal.
InternalKeyword = 8345,
//
// 摘要:
// Represents protected.
ProtectedKeyword = 8346,
//
// 摘要:
// Represents static.
StaticKeyword = 8347,
//
// 摘要:
// Represents readonly.
ReadOnlyKeyword = 8348,
//
// 摘要:
// Represents sealed.
SealedKeyword = 8349,
//
// 摘要:
// Represents const.
ConstKeyword = 8350,
//
// 摘要:
// Represents fixed.
FixedKeyword = 8351,
//
// 摘要:
// Represents stackalloc.
StackAllocKeyword = 8352,
//
// 摘要:
// Represents volatile.
VolatileKeyword = 8353,
//
// 摘要:
// Represents new.
NewKeyword = 8354,
//
// 摘要:
// Represents override.
OverrideKeyword = 8355,
//
// 摘要:
// Represents abstract.
AbstractKeyword = 8356,
//
// 摘要:
// Represents virtual.
VirtualKeyword = 8357,
//
// 摘要:
// Represents event.
EventKeyword = 8358,
//
// 摘要:
// Represents extern.
ExternKeyword = 8359,
//
// 摘要:
// Represents ref.
RefKeyword = 8360,
//
// 摘要:
// Represents out.
OutKeyword = 8361,
//
// 摘要:
// Represents in.
InKeyword = 8362,
//
// 摘要:
// Represents is.
IsKeyword = 8363,
//
// 摘要:
// Represents as.
AsKeyword = 8364,
//
// 摘要:
// Represents params.
ParamsKeyword = 8365,
//
// 摘要:
// Represents __arglist.
ArgListKeyword = 8366,
//
// 摘要:
// Represents __makeref.
MakeRefKeyword = 8367,
//
// 摘要:
// Represents __reftype.
RefTypeKeyword = 8368,
//
// 摘要:
// Represents __refvalue.
RefValueKeyword = 8369,
//
// 摘要:
// Represents this.
ThisKeyword = 8370,
//
// 摘要:
// Represents base.
BaseKeyword = 8371,
//
// 摘要:
// Represents namespace.
NamespaceKeyword = 8372,
//
// 摘要:
// Represents using.
UsingKeyword = 8373,
//
// 摘要:
// Represents class.
ClassKeyword = 8374,
//
// 摘要:
// Represents struct.
StructKeyword = 8375,
//
// 摘要:
// Represents interface.
InterfaceKeyword = 8376,
//
// 摘要:
// Represents enum.
EnumKeyword = 8377,
//
// 摘要:
// Represents delegate.
DelegateKeyword = 8378,
//
// 摘要:
// Represents checked.
CheckedKeyword = 8379,
//
// 摘要:
// Represents unchecked.
UncheckedKeyword = 8380,
//
// 摘要:
// Represents unsafe.
UnsafeKeyword = 8381,
//
// 摘要:
// Represents operator.
OperatorKeyword = 8382,
//
// 摘要:
// Represents explicit.
ExplicitKeyword = 8383,
//
// 摘要:
// Represents implicit.
ImplicitKeyword = 8384,
//
// 摘要:
// Represents yield.
YieldKeyword = 8405,
//
// 摘要:
// Represents partial.
PartialKeyword = 8406,
//
// 摘要:
// Represents alias.
AliasKeyword = 8407,
//
// 摘要:
// Represents global.
GlobalKeyword = 8408,
//
// 摘要:
// Represents assembly.
AssemblyKeyword = 8409,
//
// 摘要:
// Represents module.
ModuleKeyword = 8410,
//
// 摘要:
// Represents type.
TypeKeyword = 8411,
//
// 摘要:
// Represents field.
FieldKeyword = 8412,
//
// 摘要:
// Represents method.
MethodKeyword = 8413,
//
// 摘要:
// Represents param.
ParamKeyword = 8414,
//
// 摘要:
// Represents property.
PropertyKeyword = 8415,
//
// 摘要:
// Represents typevar.
TypeVarKeyword = 8416,
//
// 摘要:
// Represents get.
GetKeyword = 8417,
//
// 摘要:
// Represents set.
SetKeyword = 8418,
//
// 摘要:
// Represents add.
AddKeyword = 8419,
//
// 摘要:
// Represents remove.
RemoveKeyword = 8420,
//
// 摘要:
// Represents where.
WhereKeyword = 8421,
//
// 摘要:
// Represents from.
FromKeyword = 8422,
//
// 摘要:
// Represents group.
GroupKeyword = 8423,
//
// 摘要:
// Represents join.
JoinKeyword = 8424,
//
// 摘要:
// Represents into.
IntoKeyword = 8425,
//
// 摘要:
// Represents let.
LetKeyword = 8426,
//
// 摘要:
// Represents by.
ByKeyword = 8427,
//
// 摘要:
// Represents select.
SelectKeyword = 8428,
//
// 摘要:
// Represents orderby.
OrderByKeyword = 8429,
//
// 摘要:
// Represents on.
OnKeyword = 8430,
//
// 摘要:
// Represents equals.
EqualsKeyword = 8431,
//
// 摘要:
// Represents ascending.
AscendingKeyword = 8432,
//
// 摘要:
// Represents descending.
DescendingKeyword = 8433,
//
// 摘要:
// Represents nameof.
NameOfKeyword = 8434,
//
// 摘要:
// Represents async.
AsyncKeyword = 8435,
//
// 摘要:
// Represents await.
AwaitKeyword = 8436,
//
// 摘要:
// Represents when.
WhenKeyword = 8437,
//
// 摘要:
// Represents or.
OrKeyword = 8438,
//
// 摘要:
// Represents and.
AndKeyword = 8439,
//
// 摘要:
// Represents not.
NotKeyword = 8440,
//
// 摘要:
// Represents with.
WithKeyword = 8442,
//
// 摘要:
// Represents init.
InitKeyword = 8443,
//
// 摘要:
// Represents record.
RecordKeyword = 8444,
//
// 摘要:
// Represents managed.
ManagedKeyword = 8445,
//
// 摘要:
// Represents unmanaged.
UnmanagedKeyword = 8446,
//
// 摘要:
// Represents required.
RequiredKeyword = 8447,
//
// 摘要:
// Represents scoped.
ScopedKeyword = 8448,
//
// 摘要:
// Represents file.
FileKeyword = 8449,
//
// 摘要:
// Represents allows.
AllowsKeyword = 8450,
//
// 摘要:
// Represents extension.
ExtensionKeyword = 8451,
//
// 摘要:
// Represents union.
UnionKeyword = 8452,
//
// 摘要:
// Represents elif.
ElifKeyword = 8467,
//
// 摘要:
// Represents endif.
EndIfKeyword = 8468,
//
// 摘要:
// Represents region.
RegionKeyword = 8469,
//
// 摘要:
// Represents endregion.
EndRegionKeyword = 8470,
新建RoslynSyntaxParserUtil语法解析类
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SyntaxAnalysisSnake
{
/// <summary>
/// Roslyn 语法分析器:手动解析Stack无法区分代码中的花括号与字符串、注释、字符字面量中的花括号,极易产生误判。
/// Roslyn 作为官方编译器平台,能精准识别语法结构,是工业级代码分析的唯一可靠选择。
/// </summary>
public class RoslynSyntaxParserUtil
{
/// <summary>
/// 解析出代码中的花括号{}对
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public static List<Tuple<int, int>> ParseBracePairs(string code)
{
List<Tuple<int, int>> pairs = new List<Tuple<int, int>>();
//将代码文本转化为语法树SyntaxTree
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
//获取根编译单元
CompilationUnitSyntax syntaxRoot = syntaxTree.GetCompilationUnitRoot();
// 按前缀文档顺序获取子代节点(包括该节点)的列表。
foreach (SyntaxNode syntaxNode in syntaxRoot.DescendantNodesAndSelf())
{
//处理 BlockSyntax(方法体、代码块等)
if (syntaxNode is BlockSyntax blockSyntax)
{
//语法标记:起始花括号标记,结束花括号标记
SyntaxToken openToken = blockSyntax.OpenBraceToken;
SyntaxToken closeToken = blockSyntax.CloseBraceToken;
if (!openToken.IsMissing && !closeToken.IsMissing)
{
pairs.Add(Tuple.Create(openToken.SpanStart, closeToken.SpanStart));
}
}
//Microsoft.CodeAnalysis.CSharp.SyntaxKind.ObjectInitializerExpression
//处理 ObjectInitializerExpressionSyntax(对象初始化器)
if (syntaxNode is InitializerExpressionSyntax initializer)
{
//语法标记:起始花括号标记,结束花括号标记
SyntaxToken openToken = initializer.OpenBraceToken;
SyntaxToken closeToken = initializer.CloseBraceToken;
if (!openToken.IsMissing && !closeToken.IsMissing)
{
pairs.Add(Tuple.Create(openToken.SpanStart, closeToken.SpanStart));
}
}
//Microsoft.CodeAnalysis.CSharp.SyntaxKind.AnonymousObjectCreationExpression
// 处理 AnonymousObjectCreationExpressionSyntax(匿名对象)
if (syntaxNode is AnonymousObjectCreationExpressionSyntax anonymous)
{
//语法标记:起始花括号标记,结束花括号标记
SyntaxToken openToken = anonymous.OpenBraceToken;
SyntaxToken closeToken = anonymous.CloseBraceToken;
if (!openToken.IsMissing && !closeToken.IsMissing)
{
pairs.Add(Tuple.Create(openToken.SpanStart, closeToken.SpanStart));
}
}
}
return pairs;
}
/// <summary>
/// 在 Roslyn 中获取注释文本,核心在于区分文档注释(XML Documentation Comments)和普通注释(Regular Comments)。
/// 使用SyntaxNode.DescendantTrivia() + SyntaxKind 过滤
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public static List<string> GetRegularComments(string code)
{
//SemanticModel model = CSharpCompilation.CreateScriptCompilation(AppDomain.CurrentDomain.GetAssemblies()[0].FullName).GetSemanticModel(syntaxTree);
/*
* 获取普通注释 (Regular Comments)
普通注释(如 // 和 / * * /)是纯粹的语法元素,不属于语义模型。它们作为 SyntaxTrivia 存在于语法树中。
适用场景:代码格式化、注释提取、代码风格检查等仅关注代码文本的场景。
核心 API:SyntaxNode.DescendantTrivia() + SyntaxKind 过滤
获取方式:
从语法树根节点开始,使用 DescendantTrivia() 遍历所有语法标记(Trivia)。
使用 IsKind() 方法过滤出特定类型的注释。
*/
List<string> list = new List<string>();
//将代码文本转化为语法树SyntaxTree
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
SyntaxNode syntaxNode = syntaxTree.GetRoot();
//获取所有琐碎Trivia信息【注释】
IEnumerable<SyntaxTrivia> triviaList = syntaxNode.DescendantTrivia();
foreach (SyntaxTrivia trivia in triviaList)
{
//注释文本索引:trivia.Span.Start,trivia.Span.End
//获取所有单行注释 (//)
if (trivia.IsKind(SyntaxKind.SingleLineCommentTrivia))
{
list.Add(trivia.ToFullString());
}
// 获取所有多行注释 (/* */)
if (trivia.IsKind(SyntaxKind.MultiLineCommentTrivia))
{
list.Add(trivia.ToFullString());
}
// 获取所有文档注释 (///)
if (trivia.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia) || trivia.IsKind(SyntaxKind.MultiLineDocumentationCommentTrivia))
{
list.Add(trivia.ToFullString());
}
}
return list;
}
}
}
测试获取解析花括号代码段和注释文本Trivia
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SyntaxAnalysisSnake
{
class Program
{
static void Main(string[] args)
{
string code = @" string player = ""古剑四"";
/*
* 以下演示一个猜数字小游戏:
* 当用户猜数字的时候,猜中数字后游戏结束。如果没有猜中,都会提示猜大或者猜小,用于缩小数字范围
*/
Console.WriteLine(""将随机一个1~99之间的数字:请猜猜看"");
int destNumber = new Random().Next(1, 100);
int count = 0;
#region 猜数字循环流程
List<int> numberList = new List<int>();
while (true)
{
Console.WriteLine(""请猜一个数字。。。"");
count++;
int currentNumber = int.Parse(Console.ReadLine());
numberList.Add(currentNumber);
if (currentNumber == destNumber)
{
if (count >= 8) //超过7次说明没有合理规划
{
Console.WriteLine(""猜数字次数过多: 因100之内的数字对应128【2的7次方】,在已知提示猜的数字大了或者小了的提示下,最多7次就够了,而您猜了【{count}】次"");
}
Console.WriteLine($""【{player}】猜数字成功.目标数字【{destNumber}】,共猜了【{count}】次"");
break;//猜中后,游戏结束,打印结果
}
else if (currentNumber > destNumber)
{
Console.WriteLine($""当前数字【{currentNumber}】比猜的数字【大】,请重新试试,当前猜了【{count}】次"");
}
else
{
Console.WriteLine($""当前数字【{currentNumber}】比猜的数字【小】,请重新试试,当前猜了【{count}】次"");
}
}
for (int i = 0; i < numberList.Count; i++)
{
Console.WriteLine($""当前第【{i + 1}】次数字为:{numberList[i]}"");
}
#endregion
";
List<Tuple<int, int>> pairs = RoslynSyntaxParserUtil.ParseBracePairs(code);
Console.WriteLine("花括号成对个数:【" + pairs.Count + "】");
Console.WriteLine("------------------花括号详情------------------");
for (int i = 0; i < pairs.Count; i++)
{
string codeSegment = code.Substring(pairs[i].Item1, pairs[i].Item2 - pairs[i].Item1 + 1);
Console.WriteLine($"当前第【{i + 1}】对,索引区间【{pairs[i]}】:\n\x20\x20\x20\x20{codeSegment}");
}
Console.WriteLine("------------------获取注释Trivia------------------");
List<string> comments = RoslynSyntaxParserUtil.GetRegularComments(code);
for (int i = 0; i < comments.Count; i++)
{
Console.WriteLine($"当前第【{i + 1}】条注释:{comments[i]}");
}
Console.ReadLine();
}
private static void Test(string code)
{
//SyntaxAnalysisUtil.AnalysisBrace(code);
Console.WriteLine("------------解析标识符:花括号brace{}------------");
List<Tuple<int, int>> tuples = SyntaxAnalysisUtil.ParsePairSymbol(code, "{", "}");
Console.WriteLine("花括号成对个数:【" + tuples.Count + "】");
Console.WriteLine("------------------花括号详情------------------");
for (int i = 0; i < tuples.Count; i++)
{
string codeSegment = code.Substring(tuples[i].Item1, tuples[i].Item2 - tuples[i].Item1 + 1);
Console.WriteLine($"当前第【{i + 1}】对,索引区间【{tuples[i]}】:\n\x20\x20\x20\x20{codeSegment}");
}
Console.WriteLine("------------解析标识符:中括号bracket[]------------");
tuples = SyntaxAnalysisUtil.ParsePairSymbol(code, "[", "]");
Console.WriteLine("中括号成对个数:【" + tuples.Count + "】");
Console.WriteLine("------------------中括号详情------------------");
for (int i = 0; i < tuples.Count; i++)
{
string codeSegment = code.Substring(tuples[i].Item1, tuples[i].Item2 - tuples[i].Item1 + 1);
Console.WriteLine($"当前第【{i + 1}】对,索引区间【{tuples[i]}】:\n\x20\x20\x20\x20{codeSegment}");
}
Console.WriteLine("------------解析标识符:小括号parentheses()------------");
tuples = SyntaxAnalysisUtil.ParsePairSymbol(code, "(", ")");
Console.WriteLine("小括号成对个数:【" + tuples.Count + "】");
Console.WriteLine("------------------小括号详情------------------");
for (int i = 0; i < tuples.Count; i++)
{
string codeSegment = code.Substring(tuples[i].Item1, tuples[i].Item2 - tuples[i].Item1 + 1);
Console.WriteLine($"当前第【{i + 1}】对,索引区间【{tuples[i]}】:\n\x20\x20\x20\x20{codeSegment}");
}
}
}
}
运行 如图:
