<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      用Roslyn玩轉代碼之一: 解析與執行字符串表達式

      ??最近框架中的可視化界面設計需要使用到表達式引擎(解析代碼字符串并動態執行),之前舊框架的實現是將表達式字符串解析為語法樹后解釋執行該表達式,本文介紹如何使用Roslyn解析表達式字符串,并直接轉換為Linq的表達式后編譯執行。

      一、語法(Syntax)與語義(Semantic)

      ??C#的代碼通過Roslyn解析為相應的語法樹,并且利用語義分析可以獲取語法節點所對應的符號及類型信息,這樣利用這些信息可以正確的轉換為Linq的表達式。這里作者就不展開了,可以參考Roslyn文檔。

      二、實現表達式解析器(ExpressionParser)

      1. 解析字符串方法

      ??下面開始創建一個類庫工程,引用包Microsoft.CodeAnalysis.CSharp.Features,然后參照以下代碼創建ExpressionParser類, 靜態ParseCode()方法是解析字符串表達式的入口:

      using System.Linq.Expressions;
      using Microsoft.CodeAnalysis;
      using Microsoft.CodeAnalysis.CSharp;
      using Microsoft.CodeAnalysis.CSharp.Syntax;
      
      namespace ExpEngine;
      
      public sealed class ExpressionParser : CSharpSyntaxVisitor<Expression>
      {
          private ExpressionParser(SemanticModel semanticModel)
          {
              _semanticModel = semanticModel;
          }
      
          private readonly SemanticModel _semanticModel;
      
          /// <summary>
          /// 解析表達式字符串轉換為Linq的表達式
          /// </summary>
          public static Expression ParseCode(string code)
          {
              var parseOptions = new CSharpParseOptions().WithLanguageVersion(LanguageVersion.CSharp11);
              var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
                  .WithNullableContextOptions(NullableContextOptions.Enable);
      
              var tree = CSharpSyntaxTree.ParseText(code, parseOptions);
              var root = tree.GetCompilationUnitRoot();
              var compilation = CSharpCompilation.Create("Expression", options: compilationOptions)
                  .AddReferences(MetadataReference.CreateFromFile(typeof(string).Assembly.Location))
                  .AddSyntaxTrees(tree);
              var semanticModel = compilation.GetSemanticModel(tree);
              //檢查是否存在語義錯誤
              var diagnostics = semanticModel.GetDiagnostics();
              var errors = diagnostics.Count(d => d.Severity == DiagnosticSeverity.Error);
              if (errors > 0)
                  throw new Exception("表達式存在語義錯誤");
      
              var methodDecl = root.DescendantNodes().OfType<MethodDeclarationSyntax>().First();
              if (methodDecl.Body != null && methodDecl.Body.Statements.Count > 1)
                  throw new NotImplementedException("Parse block body");
      
              if (methodDecl.ExpressionBody != null)
                  throw new NotImplementedException("Parse expression body");
      
              var firstStatement = methodDecl.Body!.Statements.FirstOrDefault();
              if (firstStatement is not ReturnStatementSyntax returnNode)
                  throw new Exception("表達式方法不是單行返回語句");
      
              var parser = new ExpressionParser(semanticModel);
              return parser.Visit(returnNode.Expression)!;
          }
      }
      

      2. 解析運行時類型的方法

      ??因為轉換過程中需要將Roslyn解析出來的類型信息轉換為對應的C#運行時的類型,所以需要實現類型轉換的方法:

          private readonly Dictionary<string, Type> _knownTypes = new()
          {
              { "bool", typeof(bool) },
              { "byte", typeof(byte) },
              { "sbyte", typeof(sbyte) },
              { "short", typeof(short) },
              { "ushort", typeof(ushort) },
              { "int", typeof(int) },
              { "uint", typeof(uint) },
              { "long", typeof(long) },
              { "ulong", typeof(ulong) },
              { "float", typeof(float) },
              { "double", typeof(double) },
              { "char", typeof(char) },
              { "string", typeof(string) },
              { "object", typeof(object) },
          };
      
          /// <summary>
          /// 根據類型字符串獲取運行時類型
          /// </summary>
          private Type ResolveType(string typeName)
          {
              if (_knownTypes.TryGetValue(typeName, out var sysType))
                  return sysType;
      
              //通過反射獲取類型
              var type = Type.GetType(typeName);
              if (type == null)
                  throw new Exception($"Can't find type: {typeName} ");
      
              return type;
          }
      

      3. 解析各類語法節點轉換為對應的Linq表達式

      ??這里舉一個簡單的LiteralExpression轉換的例子,其他請參考源碼。需要注意的是Linq的表達式嚴格匹配類型簽名,比如方法調用object.Equals(object a, object b), 如果參數a是int類型,需要使用Expression.Convert(int, typeof(object))轉換為相應的類型。

          private Type? GetConvertedType(SyntaxNode node)
          {
              var typeInfo = _semanticModel.GetTypeInfo(node);
              Type? convertedType = null;
              if (!SymbolEqualityComparer.Default.Equals(typeInfo.Type, typeInfo.ConvertedType))
                  convertedType = ResolveType((INamedTypeSymbol)typeInfo.ConvertedType!);
      
              return convertedType;
          }
      
          public override Expression? VisitLiteralExpression(LiteralExpressionSyntax node)
          {
              var convertedType = GetConvertedType(node);
              var res = Expression.Constant(node.Token.Value);
              return convertedType == null ? res : Expression.Convert(res, convertedType);
          }
      

      三、測試解析與執行表達式

      ??現在可以創建一個單元測試項目驗證一下解析字符串表達式并執行了,當然實際應用過程中應緩存解析并編譯的表達式委托:

      namespace UnitTests;
      
      using static ExpEngine.ExpressionParser;
      
      public class Tests
      {
          [Test]
          public void StaticPropertyTest() => Assert.True(Run<object>("DateTime.Today") is DateTime);
      
          [Test]
          public void InstancePropertyTest() => Run<int>("DateTime.Today.Year");
      
          [Test]
          public void MethodCallTest1() => Run<DateTime>("DateTime.Today.AddDays(1 + 1)");
      
          [Test]
          public void MethodCallTest2() => Run<DateTime>("DateTime.Today.AddDays(DateTime.Today.Year)");
      
          [Test]
          public void MethodCallTest3() => Run<DateTime>("DateTime.Today.AddDays(int.Parse(\"1\"))");
      
          [Test]
          public void MethodCallTest4() => Assert.True(Run<bool>("Equals(new DateTime(1977,3,1), new DateTime(1977,3,1))"));
      
          [Test]
          public void PrefixUnaryTest() => Run<DateTime>("DateTime.Today.AddDays(-1)");
      
          [Test]
          public void NewTest() => Assert.True(Run<DateTime>("new DateTime(1977,3,16)") == new DateTime(1977, 3, 16));
      
          [Test]
          public void BinaryTest1() => Assert.True(Run<float>("3 + 2.6f") == 3 + 2.6f);
      
          [Test]
          public void BinaryTest2() => Assert.True(Run<bool>("3 >= 2.6f"));
      }
      

      四、 一些限制與TODO

      ??Linq的表達式本身存在一些限制,請參考文檔:
      https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/expression-trees/

      ??另上述代碼僅示例,比如表達式輸入參數等未實現,小伙伴們可以繼續自行完善。

      posted @ 2024-01-08 11:34  白菜園  閱讀(902)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 久久国产精品精品国产色婷婷| 青草青草视频2免费观看| 色综合AV综合无码综合网站| 国内精品综合九九久久精品| 国产自在自线午夜精品| av色国产色拍| 在线高清免费不卡全码| 久久国产精品成人免费| 无线乱码一二三区免费看| 疯狂做受XXXX高潮国产| 超碰人人超碰人人| 欧美激欧美啪啪片| 亚洲天堂一区二区三区四区| 在线高清理伦片a| 亚洲国产精品一二三区| 国产高清在线精品一区二区三区| 50路熟女| 兰溪市| 久久综合狠狠综合久久激情| 日本视频一区二区三区1| 国产成人无码AV片在线观看不卡| 日本一区二区三区免费播放视频站| 中文字幕va一区二区三区| 久久99热精品这里久久精品| 欧美日本在线一区二区三区| 人妻教师痴汉电车波多野结衣| 亚洲熟女乱色一区二区三区| 安福县| 精品熟女少妇免费久久| 无码人妻一区二区三区免费N鬼沢 亚洲国产精品自产在线播放 | 亚洲另类丝袜综合网| 久久久婷婷成人综合激情| 少妇无码av无码专区| 国产办公室秘书无码精品99| 激情六月丁香婷婷四房播| 国产无遮挡又黄又爽不要vip软件 国产成人精品一区二区秒拍1o | 在线a亚洲老鸭窝天堂| 极品少妇被猛得白浆直流草莓视频| 国产精品视频午夜福利| 国产做无码视频在线观看| 欧美福利电影A在线播放|