Почему лямбда-выражения в VB отличаются от C #? - PullRequest
11 голосов
/ 19 июля 2011

Я только что натолкнулся на ошибку в NHibernate, которая уже возникла: https://nhibernate.jira.com/browse/NH-2763

Я не уверен, относится ли это к чему-то другому, кроме перечислений, но при использовании лямбда-выражений из VB он выглядит иначе, чем тот же лямбда-код из C #.

C #:

Where(x => x.Status == EmployeeStatus.Active)

VB

Where(Function(x) x.Status = EmployeeStatus.Active)

Они такие же, насколько я знаю? (Мой VB не велик)

Если я поставлю точку останова в той же строке кода, в которую передается вышеуказанный код. В C # я получаю:

C# version

В той же строке, когда передается версия VB, я получаю:

VB version

Я что-то не так делаю? Результат один и тот же, только что по-разному отображается в C # / VB?

Edit: Итак, они отображаются по-разному, но они не могут быть одинаковыми, потому что NHibernate не может справиться с этим. Версия C # прекрасно обрабатывается NHibernate, версия VB разрешается в следующем исключении:

Exception

NHibernate StackTrace:

   at NHibernate.Impl.ExpressionProcessor.FindMemberExpression(Expression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\ExpressionProcessor.cs:line 168
   at NHibernate.Impl.ExpressionProcessor.ProcessSimpleExpression(Expression left, Expression right, ExpressionType nodeType) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\ExpressionProcessor.cs:line 323
   at NHibernate.Impl.ExpressionProcessor.ProcessSimpleExpression(BinaryExpression be) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\ExpressionProcessor.cs:line 316
   at NHibernate.Impl.ExpressionProcessor.ProcessBinaryExpression(BinaryExpression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\ExpressionProcessor.cs:line 418
   at NHibernate.Impl.ExpressionProcessor.ProcessExpression(Expression expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\ExpressionProcessor.cs:line 486
   at NHibernate.Impl.ExpressionProcessor.ProcessExpression[T](Expression`1 expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\ExpressionProcessor.cs:line 504
   at NHibernate.Criterion.QueryOver`2.Add(Expression`1 expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Criterion\QueryOver.cs:line 635
   at NHibernate.Criterion.QueryOver`2.NHibernate.IQueryOver<TRoot,TSubType>.Where(Expression`1 expression) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Criterion\QueryOver.cs:line 686
   at *removed*.EmployeeRepository.GetByEntityId(Int64 entityId, Expression`1 basicCriteria) in D:\*removed*\EmployeeRepository.cs:line 76

Значит, между ними что-то должно быть по-другому?

Редактировать 2:

Для Джонатана. Это метод, в котором используется выражение:

public IEnumerable<Employee> GetByEntityId(long entityId, Expression<Func<Employee, bool>> basicCriteria)
{
    IEnumerable<Employee> result;

    using (var tx = Session.BeginTransaction())
    {
        var employeeQuery = Session.QueryOver<Employee>()
                                    .Where(x => x.EntityId == entityId);

        if (basicCriteria != null)
            employeeQuery = employeeQuery.Where(basicCriteria);

        result = employeeQuery.List();

        tx.Commit();
    }

    return result;
}

Ответы [ 3 ]

10 голосов
/ 19 июля 2011

Разница, которую вы видите, не имеет ничего общего с лямбдами; это просто разница в семантике языков. VB отправляет вызовы к функциям, которые по умолчанию генерируют исключения, если переполняется целое число (следовательно, Checked часть имени).

По умолчанию компилятор C # не выдает «проверенную» версию функций, и, очевидно, NHibernate разработан пользователями C #, поэтому он, похоже, не распознает «проверенные» функции.

Если вы перейдете к параметрам компиляции для своего проекта и нажмете «Дополнительные параметры компиляции», вы можете установить флажок «Удалить проверки целочисленного переполнения», чтобы в VB было поведение по умолчанию на C #, и вы больше не должны получать эту ошибку:

Screenshot of dialog showing option

5 голосов
/ 19 июля 2011

Часть с <>__DisplayClass означает, что компилятор создал замыкание. Это означает, что выражение в отладчике не то, которое вы показали, а что-то вроде

var status = EmployeeStatus.Active;
Expression<Func<Employee, bool>> expr = x => x.Status == status;

Но это не та проблема, с которой NHibernate испытывает проблемы. Разница между Convert и ConvertChecked есть. И это вызвано разницей в семантике между C # и VB.NET:

В C # по умолчанию все вычисления во время выполнения не проверяются, то есть они не проверяются на арифметические переполнения. Вы можете изменить значение по умолчанию для определенного фрагмента кода, используя checked.

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

Итак, следующий код C # создает ту же лямбду, что и ваш VB:

checked
{
    Expression<Func<Employee, bool>> expr = x => x.Status == EmployeeStatus.Active;
}

РЕДАКТИРОВАТЬ: Если вы не нашли другой вариант, в качестве крайней меры вы можете переписать выражение, которое VB.NET генерирует в форму, используя Convert вместо ConvertChecked:

Class UncheckedVisitor
    Inherits ExpressionVisitor

    Protected Overrides Function VisitUnary ( _
        node As UnaryExpression _
    ) As Expression
        If node.NodeType = ExpressionType.ConvertChecked
            node = Expression.Convert(node.Operand, node.Type, node.Method)
        End If
        Return MyBase.VisitUnary(node)
    End Function
End Class

unchechedVisitor.Visit(expr) затем возвращает expr со всеми экземплярами ConvertChecked, замененными на Convert.

2 голосов
/ 19 июля 2011

Да, он просто отображается по-другому. Вы не делаете ничего плохого. Встроенные методы VB имеют другой синтаксис, а также интеграцию с IDE

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...