当我使用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", "||")
但这有明显的弱点,看起来很尴尬。 另外,我不想创建一个大的“替换”部分或巨大的正则表树,只是为了使格式正确。
是否有一种简单的方法可以获得类似于人类可读形式的表达式树?
答案 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抽象化。