Разбор дерева выражений, переменные заканчиваются константами - PullRequest
3 голосов
/ 13 января 2011

Итак, я работаю над проектом, в котором мне нужно разобрать дерево выражений.У меня почти все работает, но я столкнулся с небольшой проблемой.

Я смотрел другие вопросы о StackOverflow на деревьях выражений, но, похоже, не нашел ответана мой вопрос, так что здесь.

Моя проблема заключается в разнице (или отсутствии) между константами и переменными.Позвольте мне начать с примера:

user => user.Email == email

Это явно не константа, а переменная, но в результате получается выражение ConstantExp где-то в дереве выражений.Если вы посмотрите на само выражение, оно выглядит немного странно:

Expression = {value(stORM.Web.Security.User+<>c__DisplayClassa)}

Если мы возьмем другой пример:

task => task.Status != TaskStatus.Done && t.Status != TaskStatus.Failed

Здесь я использую ENUM (TaskStatus).

Итак, моя проблема в том, что при синтаксическом анализе дерева мне кажется, что в обоих случаях я получаю выражение ConstantExp, и мне действительно нужно уметь их различать.Это всего лишь примеры, поэтому то, что я спрашиваю, - это общий способ отличить эти два типа выражений друг от друга, поэтому я могу обрабатывать их двумя различными способами при разборе.

РЕДАКТИРОВАТЬ: хорошо, мойпримеры могут быть неясными, поэтому я попробую еще раз.Первый пример:

Пользователь user = db.Search (u => u.Email == электронная почта);

Я пытаюсь найти пользователя с данным адресом электронной почтыадрес.Я анализирую это в хранимой процедуре, но это помимо того, что я думаю.

Второй пример:

IList tasks = db.Search (t => t.Status!= TaskStatus.Done && t.Status! = TaskStatus.Failed);

И здесь я пытаюсь найти все задачи со статусом, отличным от Готово и Не выполнено.Опять же, это разбор в хранимую процедуру.В первом примере мой код должен определить, что хранимой процедуре нужен входной параметр, значение переменной электронной почты.Во втором примере мне не нужны никакие входные параметры, мне просто нужно создать SQL для выбора задачи со статусом, отличным от Готово и Сбой.

Еще раз спасибо

Ответы [ 4 ]

3 голосов
/ 13 января 2011

Так что с точки зрения выражения значение является константой. Это не может быть изменено выражением.

То, что у вас есть, является потенциально открытым замыканием - то есть значение может меняться между выполнениями выражения, но не во время него. Так что это «константа». Это различие между миром функционального программирования и нефункционального программирования :).

Рассмотрим

        int a =2;
        Expression<Func<int, int>> h = x=> x+ a;
        Expression<Func<int, int>> j = x => x +2;
        a = 1;

термин a является доступом члена к анонимному классу, который оборачивает и обращается к переменной в стеке. Первый узел - это узел MemberAccess, затем под ним - выражение является константой.

Для кода выше:

((SimpleBinaryExpression)(h.Body)).Right
{value(WindowsFormsApplication6.Form1+<>c__DisplayClass0).a}
    CanReduce: false
    DebugView: ".Constant<WindowsFormsApplication6.Form1+<>c__DisplayClass0>(WindowsFormsApplication6.Form1+<>c__DisplayClass0).a"
    Expression: {value(WindowsFormsApplication6.Form1+<>c__DisplayClass0)}
    Member: {Int32 a}
    NodeType: MemberAccess
    Type: {Name = "Int32" FullName = "System.Int32"}

И константа под ней:

((MemberExpression)((SimpleBinaryExpression)(h.Body)).Right).Expression
{value(WindowsFormsApplication6.Form1+<>c__DisplayClass0)}
    CanReduce: false
    DebugView: ".Constant<WindowsFormsApplication6.Form1+<>c__DisplayClass0>(WindowsFormsApplication6.Form1+<>c__DisplayClass0)"
    NodeType: Constant
    Type: {Name = "<>c__DisplayClass0" FullName = "WindowsFormsApplication6.Form1+<>c__DisplayClass0"}
    Value: {WindowsFormsApplication6.Form1.}
        }
    }

Старая добрая 2 выходит на:

((SimpleBinaryExpression)(j.Body)).Right
{2}
    CanReduce: false
    DebugView: "2"
    NodeType: Constant
    Type: {Name = "Int32" FullName = "System.Int32"}
    Value: 2

Так что я не знаю, помогает ли это вам или нет. Вы можете сказать, посмотрев на родительский узел - или тип объекта, к которому обращается родительский узел.


Добавление в результате вашего уточнения -

поэтому, когда вы говорите

user => user.Email == email

Вы имеете в виду, что ищите всех пользователей с электронной почтой, равной переданному параметру - однако это выражение ссылки означает что-то совсем другое.

что вы хотите сказать, это

Expression<Func<User, string, bool>> (user, email) => user.Email == email

Таким образом, электронная почта теперь будет параметром. Если вам это не нравится, есть еще одна вещь, которую вы можете сделать.

Второй пример будет работать просто отлично - никаких дополнительных параметров не требуется.

t => t.Status != TaskStatus.Done && t.Status != TaskStatus.Failed

Редактировать: добавив еще один способ:

Итак, одна из вещей, которые вы должны были сделать, чтобы ваш код работал, было объявить строковое электронное письмо за пределами лямбды - это немного неуклюже.

Вы могли бы лучше определить параметры, поместив их в определенное место, например, в статический класс. Затем, проходя через лямбду, вам не нужно смотреть на какой-то ужасный объект укрытия - но хороший статический класс вашего творения.

public static class Parameter
{
    public static T Input<T>(string name)
    {
        return default(T);
    }  
}

Тогда ваш код выглядит так:

Expression<Func<User, bool>> exp = x => x.Email == Parameter.Input<String>("email");

Затем вы можете пройтись по дереву - когда вы приходите к вызову статического класса Parameter, вы можете посмотреть на тип и имя (в коллекции аргументов), и вы начнете ....

2 голосов
/ 13 января 2011

Захваченная переменная (первый случай с email) обычно представляет собой ConstantExpression, представляющий экземпляр класса захвата , с MemberExpression до FieldInfo для «переменной» - как если у вас было:

private class CaptureClass {
    public string email;
}
...
var obj = new CaptureClass();
obj.email = "foo@bar.com";

Здесь obj - константа внутри выражения.

Итак: если вы видите от MemberExpression (поля) до ConstantExpression, это вероятно захваченная переменная. Вы также можете проверить CompilerGeneratedAttribute в классе захвата ...

A литерал константа обычно будет , просто будет ConstantExpression; на самом деле, было бы трудно придумать сценарий, в котором вы используете член константы, если только вы не могли бы что-то вроде:

() => "abc".Length

но здесь .Length - это свойство (не поле), и строка, вероятно, не имеет [CompilerGenerated].

2 голосов
/ 13 января 2011

Имя немного неудачное, на самом деле оно не является константой.

Это просто относится к значению вне выражения.

0 голосов
/ 15 марта 2011

Просто проверьте тип выражения константы.Любое «постоянное» ConstantExpression имеет примитивный тип.

...