开源C#代码解析Microsoft.CodeAnalysis解析代码块以及注释文本SyntaxTrivia

右键项目,管理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}");
            }
        }
    }
}

运行 如图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

斯内科

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值