当我对表达式所代表的代码的语义感兴趣时,而不是精确的语法树时,我发现将其编译为汇编并在ILSpy中查看非常有用。方便方法:
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翻译和IL -> C#反编译的两个步骤。然而,它可以通过将一些goto转换为循环以及生成实际的C#来提高可读性。
如果Expression
包含“非平凡常量”(活动对象),则此方法会失败;但是,可以编写一个访问者,将常量替换为新变量,然后在顶层对这些变量进行lambda抽象化。
String.Replace
方法。如果你担心可能会出现类似的变量名误报,可以尝试匹配周围的空格.Replace(" AndAlso ", " && ")
。编辑:关于你为什么要寻找替代方案,只需将此转换行为放入共享实用程序方法中即可。这样,如果您需要添加新的“Replace”,可以在一个位置完成。 - Chris Sinclair