【问题标题】:ToString on Expression Trees produces badly formatted output表达式树上的 ToString 产生格式错误的输出
【发布时间】:2014-06-19 17:29:53
【问题描述】:

当我使用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", "||")

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

有没有一种简单的方法来获得类似代码的人类可读形式的表达式树?

【问题讨论】:

  • 由于表达式不是 C# 特定的,我不知道有另一种方法让它输出 C#-exact 语法。我认为最好的方法是确实使用String.Replace 方法。如果您担心类似的变量名称可能出现误报,请尝试与周围的空格匹配.Replace(" AndAlso ", " && ") 编辑:关于为什么您正在寻找替代品,只需将此转换行为放入共享实用程序方法。这样,如果您需要添加一个新的 Replace,您可以在一个地方完成。

标签: c# expression-trees tostring


【解决方案1】:

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

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

【讨论】:

【解决方案2】:

当我对表达式所代表的代码的语义感兴趣时,我发现将它编译成一个程序集并在其中查看它非常有用间谍。便捷方法:

// 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 完成的 IL -> C# 反编译。OTOH,它可以通过转换一些来提高可读性进入循环,并生成实际的 C#。)

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

【讨论】:

    猜你喜欢
    • 2015-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多