如何将Expression转换为CSharpCompilation或CSharpSyntaxTree?

时间:2018-02-14 15:16:50

标签: c# .net roslyn

如何转换:

System.Linq.Expression.Expression

分为:

Microsoft.CodeAnalysis.CSharp.CSharpCompilation

或者进入:

Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree

我需要下一个特定案例才能像其中一个选项一样工作:

  • 我可以将ExpressionCSharpSyntaxTree编译为可执行代码的相同行为

  • 当我查看手动输入的C#表达式时,我可以获得CSharpSyntaxTree并生成相同的代码。

    public void MultipleStatementsBlockTest()
    {
        var p = Expression.Parameter(typeof(int), "p");
        Expression assignment = Expression.Assign(p, Expression.Constant(1));
        Expression addAssignment = Expression.AddAssign(p, Expression.Constant(5));
        // Convert addAssignment  to Roslyn tree here
    }
    
    class HasIndexers
    {
        public object this[string s] => null;
    
        public object this[int i] => null;
    }
    public void CanPrettyPrintVariousIndexers()
    {
        Expression<Func<Bool>> expr = () => new HasIndexers()[3] == new HasIndexers()["three"];
        // Convert expr to Roslyn tree here
    }
    

更新:

以不可接受的方式处理Expression -> string -> Roslyn。转换应该是直接的。

UPDATE2: 可能的用法:

  1. DI / IoC容器或ORM或消息总线或其他库,基于运行时表达式进入编译时库,代码生成。
  2. 一个。开始更快

    湾编译时间错误,而不是运行时错误。

    ℃。可能更快的运行时间。

    d。通过吃F#饼,让C#活得更长。

    即可能更多的杂交文库,例如用于矩阵(图像)操作,允许复制和粘贴在Server / Desktop上创建的结果树作为在IoT上使用的代码。

    1. 表达式lib(例如用于调试),它将这些转换为C#代码。
    2. 一个。更多代码输出选项(名称空间,空格,制表符)。 湾使用较少的手动代码生成更正确。 C。支持不同的输出语言而不是C#。

2 个答案:

答案 0 :(得分:3)

请参阅here syntax transformation

  

因为语法树是不可变的,所以Syntax API没有提供在构造之后修改现有语法树的直接机制。但是,Syntax API确实提供了基于对现有树的指定更改来生成新树的方法。从SyntaxNode派生的每个具体类都定义了With *方法,您可以使用这些方法指定对其子属性的更改。

     

此外,ReplaceNode扩展方法可用于替换子树中的后代节点。如果没有此方法,更新节点还需要手动更新其父节点以指向新创建的子节点并在整个树中重复此过程

     
      
  • 一个称为重新旋转树的过程。
  •   

示例 - 使用With *和ReplaceNode方法进行转换:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace ConstructionCS
{
    class Program
    {
        static void Main(string[] args)
        {
            NameSyntax name = IdentifierName("System");
            name = QualifiedName(name, IdentifierName("Collections"));
            name = QualifiedName(name, IdentifierName("Generic"));

            SyntaxTree tree = CSharpSyntaxTree.ParseText(
@"using System;
using System.Collections;
using System.Linq;
using System.Text;

namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(""Hello, World!"");
        }
    }
}");

            var root = (CompilationUnitSyntax)tree.GetRoot();

            var oldUsing = root.Usings[1];
            var newUsing = oldUsing.WithName(name);

            root = root.ReplaceNode(oldUsing, newUsing);
        }
    }
}

现场试用:http://roslynquoter.azurewebsites.net/

答案 1 :(得分:2)

Expression转换为Roslyn SyntaxTree的最简单方法是:

  1. Expression转换为相应的源代码。
  2. 使用CSharpSyntaxTree.ParseText()解析表达式源代码。
  3. 我们基本上减少了将Expression转换为其源代码的问题。已经问过Such question。回答史蒂夫威尔克斯proposed他的图书馆AgileObjects.ReadableExpressions。 它基本上在ToReadableString()上提供了一种扩展方法Expression

    // str will contain source code of expression
    var str = expression.ToReadableString();
    

    我用不同的表达方式尝试过这个库,它的工作原理非常好。

    所以回到你的问题,基于这个库的解决方案将非常简单:

    1. 安装AgileObjects.ReadableExpressions NuGet包。
    2. Expression上定义以下扩展方法:

      public static class ExpressionExtensions
      {
            public static SyntaxTree ToSyntaxTree(this Expression expression)
            {
                  var expressionCode = expression.ToReadableString();
                  return CSharpSyntaxTree.ParseText(expressionCode);
            }
      }
      
    3. 现在你可以尝试一下:

      var p = Expression.Parameter(typeof(int), "p");
      Expression assignment = Expression.Assign(p, Expression.Constant(1));
      Expression addAssignment = Expression.AddAssign(p, Expression.Constant(5));
      BlockExpression addAssignmentBlock = Expression.Block(
          new ParameterExpression[] { p },
          assignment, addAssignment);
      
      SyntaxTree tree = addAssignmentBlock.ToSyntaxTree();
      

      此类表达式将转换为以下代码:

      var p = 1;
      p += 5;
      

      已成功解析为对应的SyntaxTree