Почему лямбда-выражения в VB отличаются от С#?
Я только что наткнулся на ошибку в NHibernate, которая, возможно, уже поднята:
https://nhibernate.jira.com/browse/NH-2763
Я не уверен, что это относится ко всему, кроме других, но при использовании Lambda из VB оно выглядит иначе, чем Lambda с С#.
С#:
Where(x => x.Status == EmployeeStatus.Active)
В. Б.
Where(Function(x) x.Status = EmployeeStatus.Active)
Они такие же, насколько мне известно? (Мой VB не велик)
Если я помещаю точку останова в ту же строку кода, в которую передается приведенный выше код. В С# я получаю:
![C# version]()
В той же строке, когда передается версия VB, я получаю:
![VB version]()
Я что-то делаю неправильно? Является ли результат тем же самым, только что отображаемым между С#/VB?
Edit:
Хорошо, поэтому они отображаются разные, но они не могут быть одинаковыми, потому что NHibernate не может справиться с этим. Версия С# отлично обрабатывается 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;
}
Ответы
Ответ 1
Разница, которую вы видите, не имеет ничего общего с лямбдами; это просто разница в семантике языков. VB испускает вызовы функций, которые по умолчанию генерируют исключения, если целое число переполняется (следовательно, часть Checked
).
По умолчанию компилятор С# не выделяет "проверенную" версию функций, и, по-видимому, NHibernate разрабатывается пользователями С#, поэтому он, похоже, не распознает "проверенные" функции.
Если вы перейдете к параметрам "Компиляция" для своего проекта и нажмите "Дополнительные параметры компиляции", вы можете проверить флажок "Исключить проверку переполнения целого", чтобы VB имел поведение С# по умолчанию, и вы больше не должны получать эту ошибку:
![Screenshot of dialog showing option]()
Ответ 2
Часть с <>__DisplayClass
означает, что компилятор создал закрытие. Это означает, что выражение в отладчике - это не тот, который вы показывали, а нечто вроде
var status = EmployeeStatus.Active;
Expression<Func<Employee, bool>> expr = x => x.Status == status;
Но это не та часть, с которой у NHibernate возникают проблемы. Разница между Convert
и ConvertChecked
равна. И это вызвано различием в семантике между С# и VB.NET:
В С# по умолчанию все вычисления во время выполнения не отмечены, то есть они не проверяются на арифметические переполнения. Вы можете изменить значение по умолчанию для определенного фрагмента кода с помощью checked
.
В VB по умолчанию выполняется проверка вычислений, что приводит к разным сгенерированным лямбда. Я уверен, что есть способы изменить это и на VB.
Итак, следующий код С# создает тот же самый лямбда, что и ваш 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
.
Ответ 3
Да, он просто отображается другим.
Вы ничего не делаете неправильно. Встраиваемые методы VB имеют другой синтаксис, а также интеграцию IDE