表达式树上的ToString产生格式错误的输出

时间:2014-05-02 22:55:16

标签: c# expression-trees tostring

当我使用Expression.ToString()将表达式树转换为人类可读形式时,结果是这样的:

x => ((x.ID > 2) OrElse (x.ID != 6))
x => ((x.ID > 2) AndAlso (x.ID != 6))

理想情况下,我希望输出显示运算符而不是“OrElse”和“AndAlso”:

x => ((x.ID > 2) || (x.ID != 6))
x => ((x.ID > 2) && (x.ID != 6))

作为一种解决方法,我可以使用string.Replace()方法..

.Replace("AndAlso", "&&")
.Replace("OrElse", "||")

但这有明显的弱点,看起来很尴尬。 另外,我不想创建一个大的“替换”部分或巨大的正则表树,只是为了使格式正确。

是否有一种简单的方法可以获得类似于人类可读形式的表达式树?

2 个答案:

答案 0 :(得分:0)

不幸的是,正确执行此操作的最简单方法是编写自己的ExpressionVisitor类,该类生成C#格式化的输出代码。

最简单的方法是使用参考源中的ExpressionStringBuilder作为起点并进行调整,直到您对输出结果满意为止。

答案 1 :(得分:0)

当我对由表达式表示的代码的语义而不是确切的语法树感兴趣时,我发现将其编译为Assembly并在其中查看它非常有用。 ILSpy。便捷方法:

// Code is probably adapted from some other answer, don't remember
public static void CompileToAssemblyFile(
  this LambdaExpression expression,
  string outputFilePath = null,
  string assemblyAndModuleName = null,
  string typeName = "TheType",
  string methodName = "TheMethod",
  // Adjust this
  string ilSpyPath = @"C:\path\to\ILSpy.exe")
{
  assemblyAndModuleName = assemblyAndModuleName ?? nameof(CompileToAssemblyFile);

  outputFilePath = outputFilePath ??
                   Path.Combine(
                     Path.GetTempPath(),
                     $"{assemblyAndModuleName}_{DateTime.Now:yyyy-MM-dd_HH_mm_ss}_{Guid.NewGuid()}.dll");

  var domain = AppDomain.CurrentDomain;
  var asmName = new AssemblyName {Name = assemblyAndModuleName};

  var asmBuilder = domain.DefineDynamicAssembly(
    asmName,
    AssemblyBuilderAccess.RunAndSave,
    Path.GetDirectoryName(outputFilePath));

  string outputFileName = Path.GetFileName(outputFilePath);

  var module = asmBuilder.DefineDynamicModule(
    assemblyAndModuleName,
    outputFileName,
    true);

  var typeBuilder = module.DefineType(typeName, TypeAttributes.Public);

  var methodBuilder = typeBuilder.DefineMethod(
    methodName,
    MethodAttributes.Public | MethodAttributes.Static,
    expression.ReturnType,
    expression.Parameters.Select(p => p.Type).ToArray());

  var pdbGenerator = DebugInfoGenerator.CreatePdbGenerator();

  expression.CompileToMethod(methodBuilder, pdbGenerator);

  typeBuilder.CreateType();

  asmBuilder.Save(outputFileName);

  Process.Start(ilSpyPath, outputFilePath);
}

(这对语法树不是很忠实,因为它同时经历了LambdaCompiler完成的Expression-> IL转换和ILSpy。OTOH的IL-> C#反编译,它可以通过以下方式提高可读性:将一些goto转换成循环,并通过生成实际的C#。)

如果Expression包含“非平凡常量”(活动对象),则此操作将失败;但是为此,可以编写一个访客,用新变量替换常量,然后在顶层进行lambda抽象化。