Ваш второй делегат не переписывает первый в формате анонимного делегата (а не лямбда).Посмотрите на ваши условия.
Первый:
x.ID == packageId || x.Parent.ID == packageId || x.Parent.Parent.ID == packageId
Второй:
(x.ID == packageId) || (x.Parent != null && x.Parent.ID == packageId) ||
(x.Parent != null && x.Parent.Parent != null && x.Parent.Parent.ID == packageId)
При вызове лямбды будет выдано исключение для любого x
, где идентификаторне совпадает, и родительский элемент является нулевым или не совпадает, а дедушка является нулевым.Скопируйте нулевые проверки в лямбду, и она должна работать правильно.
Редактировать после комментария к вопросу
Если ваш исходный объект не является List<T>
, то у нас нет возможности узнать, чтотип возвращаемого значения FindAll()
есть, и реализует ли это интерфейс IQueryable
.Если это так, то это, вероятно, объясняет расхождение.Поскольку лямбды могут быть преобразованы во время компиляции в Expression<Func<T>>
, но анонимные делегаты не могут , тогда вы можете использовать реализацию IQueryable
при использовании лямбда-версии, но LINQ-to-Objects при использовании анонимноговерсия делегата.
Это также объясняет, почему ваша лямбда не вызывает NullReferenceException
.Если бы вы передали это лямбда-выражение чему-то, что реализует IEnumerable<T>
, но , а не IQueryable<T>
, оценка лямбды во время выполнения (которая ничем не отличается от других методов, анонимных или нет) выдаст NullReferenceException
в первый раз он столкнулся с объектом, где ID
не был равен цели, а родитель или дедушка были нулевыми.
Добавлено 16.03.2011, 8:29 EDT
Рассмотримследующий простой пример:
IQueryable<MyObject> source = ...; // some object that implements IQueryable<MyObject>
var anonymousMethod = source.Where(delegate(MyObject o) { return o.Name == "Adam"; });
var expressionLambda = source.Where(o => o.Name == "Adam");
Эти два метода дают совершенно разные результаты.
Первый запрос - это простая версия.Результатом анонимного метода является делегат, который затем передается методу расширения IEnumerable<MyObject>.Where
, где все содержимое source
будет проверено (вручную в памяти с использованием обычного скомпилированного кода) для вашего делегата.Другими словами, если вы знакомы с блоками итераторов в C #, это примерно так:
public IEnumerable<MyObject> MyWhere(IEnumerable<MyObject> dataSource, Func<MyObject, bool> predicate)
{
foreach(MyObject item in dataSource)
{
if(predicate(item)) yield return item;
}
}
Существенным моментом здесь является то, что вы фактически выполняете фильтрацию в памяти на стороне клиента.Например, если бы вашим источником был какой-то SQL ORM, в запросе не было бы предложения WHERE
;весь набор результатов будет возвращен клиенту и отфильтрован там .
Второй запрос, который использует лямбда-выражение, преобразуется в Expression<Func<MyObject, bool>>
и использует IQueryable<MyObject>.Where()
метод расширения.Это приводит к объекту, который также напечатан как IQueryable<MyObject>
.Все это работает, передав выражение базовому поставщику. Вот почему вы не получаете NullReferenceException
.Поставщик запросов полностью должен преобразовать выражение (которое, вместо того, чтобы быть фактически скомпилированной функцией, которую она может просто вызвать, является представлением логики выражения, использующего объекты), во что-то, что оно можетиспользовать.
Простой способ увидеть различие (или, по крайней мере, то, что есть ) различие, было бы сделать вызов AsEnumerable()
перед вашим вызовом Where
в лямбда-версии.Это заставит ваш код использовать LINQ-to-Objects (это означает, что он работает с IEnumerable<T>
, как версия с анонимным делегатом, а не с IQueryable<T>
, как в настоящее время с лямбда-версией), и вы получите ожидаемые исключения.
TL; DR версия
Суть и недостаток в том, что ваше лямбда-выражение переводится в какой-то запрос к вашему источнику данных, тогда как версия анонимного метода оценивает весь источник данных в памяти.Что бы вы ни делали, перевод вашей лямбды в запрос не отражает ожидаемой вами логики, поэтому он не дает ожидаемых результатов.