在C#表达式树中可靠地检测编译器生成的类

12

我正在构建一个类似于Linq-to-SQL的C#表达式转Javascript代码的转换器,但是我在处理编译器生成的表达式树时遇到了问题。

我面临的具体问题是如何处理编译器生成的MemberExpression值,但是它们的类型上没有指定CompilerGeneratedAttribute

以下是我一直尝试的简化版本:

void ProcessMemberExpression(MemberExpression memberX) {
    var expression = memberX.Expression;
    var expressionType = expression.Type;
    var customAttributes = expressionType.GetCustomAttributes(true);
    var expressionTypeIsCompilerGenerated = customAttributes.Any(x => x is CompilerGeneratedAttribute);
    if (expressionTypeIsCompilerGenerated) {
        var memberExpressionValue = Expression.Lambda(memberX).Compile().DynamicInvoke();
        ... do stuff ...
    }
    else {
        ... do other stuff ...
    }
}

现在,我正在使用Visual Studio进行调试,并发现了以下内容(在即时窗口中运行):

expressionType.Name
"<>c__DisplayClass64"
expressionType.GetCustomAttributes(true)
{object[0]}
expressionType.GetCustomAttributes(true).Length
0

这里我有一个明显由编译器生成的类,没有自定义属性,因此没有 CompilerGeneratedAttribute! 因此,我的代码将执行其他操作,而我希望它只执行 stuff

如果有人能帮我解决这个问题,我将非常感激。 如果可能的话,我真的不想像匹配expressionType.Name<>.*__DisplayClass 这样的东西一样做任何肮脏的事情。


2
任何在 C# 中无效的命名 :) 编译器故意使用在 C# 中不合法但在 IL 中合法的名称,以确保它不会与实际源代码中的任何内容发生冲突。 - James Manning
谢谢,詹姆斯。我希望有一种比询问“类型名称是否与此特定正则表达式不匹配”更少可怕的方法来发现这些情况。 - Rafe
注意:如果使用Reflection.Emit表达式,则不会生成任何类。 - leppie
@Rafe 但是你并没有真正回答我的问题:为什么你必须对编译器生成的类型这样做,而对普通类型则不需要?对于你来说,这两者之间有什么区别? - svick
@svick 对不起,也许我应该更明确一些。我正在编写一个C#表达式树到JavaScript的“编译器”。例如,我希望将C#表达式() => x + 1 - y转换为JavaScript表达式X + 1 - Y,其中X和Y是C#名称x和y在JavaScript端的名称。现在,有时C#编译器会使用生成的类隐藏常量1(好吧,这实际上发生在我身上的是bool类型,但这是小细节),我无法检查这些类。相反,我需要评估这些术语并发出JS以获取结果C# ,而不是原始表达式。 - Rafe
显示剩余3条评论
1个回答

4

天哪,这太惨了!第三方的 Linq-to-SQL 转换器真的是这样解决问题的吗?我发现他们不严格遵循 CompilerGeneratedAttribute 的方法真是很令人惊讶。噢,好吧 - 感谢您提供的链接,Skeet 在这方面通常是正确的。 - Rafe
谢谢你的指引,虽然我还没有在Matt的代码中找到我的问题的答案。我之前想说的是,我无法相信微软不严格遵守使用他们的CompilerGeneratedAttribute。这真是令人沮丧。 - Rafe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接