Работает ли Expression.ToString()?

У меня есть сгенерированная лямбда, но когда я хочу посмотреть, как обычная лямбда, она просто ничего не показывает. Когда я звоню expr.Body.ToString(), я получаю следующее:

{var compareA; ... }

Но DebugView для выражения отлично работает:

.Lambda #Lambda1<System.Comparison`1[XLinq.Test.Comparers.CustomComparerTest+Test]>(
    XLinq.Test.Comparers.CustomComparerTest+Test $x,
    XLinq.Test.Comparers.CustomComparerTest+Test $y) {
    .Block(System.Int32 $compareA) {
        $compareA = .Call ($x.A).CompareTo($y.A);
        .If ($compareA != 0) {
            .Return #Label1 { $compareA }
        } .Else {
            .Block(System.Int32 $compareB) {
                $compareB = .Call ($x.B).CompareTo($y.B);
                .If ($compareB != 0) {
                    .Return #Label1 { $compareB }
                } .Else {
                    .Block(System.Int32 $compareC) {
                        $compareC = .Call ($x.C).CompareTo($y.C);
                        .If ($compareC != 0) {
                            .Return #Label1 { $compareC }
                        } .Else {
                            .Block(System.Int32 $compareD) {
                                $compareD = .Call ($x.D).CompareTo($y.D);
                                .If ($compareD != 0) {
                                    .Return #Label1 { $compareD }
                                } .Else {
                                    .Default(System.Void)
                                }
                            }
                        }
                    }
                }
            }
        };
        .Label
            0
        .LabelTarget #Label1:
    }
}

Почему я получаю этот результат?


person Alex Zhukovskiy    schedule 06.12.2015    source источник


Ответы (1)


Это связано с тем, что переопределение Expression.ToString основано на внутреннем типе посетителя ExpressionStringBuilder, который создает значительно упрощенное представление дерева выражений.

Представление отладки, предоставляемое пользовательскими прокси-серверами отладчика, определенными для каждого типа, производного от Expression (т. е. [DebuggerTypeProxy(typeof(Expression.BlockExpressionProxy))] для BlockExpression), предоставляет гораздо больше информации, как вы уже выяснили, предоставляя вывод более подробного DebugViewWriter посетителя (также внутреннего).

К сожалению, вы не можете легко получить этот вывод вне сценариев отладки, если только вы не хотите использовать отражение для получения значения частного свойства DebugView (определенного в System.Linq.Expressions.Expression) следующим образом:

Expression<Func<string, int>> expr = str => str.Length;
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
PropertyInfo debugViewProp = typeof(Expression).GetProperty("DebugView", flags);
MethodInfo debugViewGetter = debugViewProp.GetGetMethod(nonPublic: true);
string debugView = (string)debugViewGetter.Invoke(expr, null);

Производит

.Lambda #Lambda1<System.Func`2[System.String,System.Int32]>(System.String $str) {
    $str.Length
}

Как всегда, Reference Source — ваш лучший друг:

http://referencesource.microsoft.com/#System.Core/Microsoft/Scripting/Ast/Expression.cs,aa5f054356a8a17d

person Kirill Shlenskiy    schedule 06.12.2015
comment
Поэтому я не могу получить строковое представление Expression, написанное на С#? Я имею в виду что-то вроде var compareA = x.CompareTo(y); ... - person Alex Zhukovskiy; 07.12.2015
comment
Правильно, ToString выводит только переменные, объявленные BlockExpression. Он не посещает внутренние выражения (они представлены ...). Вы можете использовать сторонний пакет, чтобы делать то, что вы хотите (ExpressionToCode является одним из примеров — я не одобряю его, но быстрый тест показывает, что он выполняет свою работу, хотя и без каких-либо отступов). В качестве альтернативы вы можете создать своего собственного посетителя — аналогично написанию IQueryable провайдера. - person Kirill Shlenskiy; 08.12.2015
comment
@AlexZhukovsky Я написал библиотеку, которая обеспечивает лучшее представление C#, а также другие форматы, такие как как вызовы фабричных методов. - person Zev Spitz; 19.12.2019