2014-01-08 4 views
3

У меня есть 2 списков obj1 и obj2Каким образом CLR интерпретирует следующий LINQ запрос

var list1 = new List<obj1>(); 
var list2 = new List<obj2>(); 

obj1 и obj2 разделяет строковое свойство с именем имени, мне нужно фильтровать песни1 значений имени свойства, которые доступны в списке2, поэтому я сделал следующее:

var filteredlist = list1.Where(o => list2.Select(o2 => o2.name) 
             .Distinct() 
             .Contains(o.name)); 

- вышеупомянутый запрос Linq эквивалентен следующему?

var distinctNames = list2.Select(o2 => o2.name).Distinct(); 
var filteredlist = list1.Where(o => distinctNames.Contains(o.name)); 

мой вопрос в первом запросе делает CLR сделать переменную Темп провести distinctNames, даже если я не создавал его сам, как во втором запросе? или он будет переделать Select Distinct из списка2 с каждой итерацией?

, если он не создает временную переменную, как бы вы могли написать этот запрос в одной строке?

ответ

8

LINQ - это только набор методов расширения, определенных на интерфейсе IEnumerable, поэтому речь идет не о том, «как это делает CLR». Ваш запрос LINQ будет эквивалентно

Func<bool> innerAction = list2.Select(o2 => o2.name).Distinct().Contains(o => o.name); 
foreach(var e1 in list1) 
{ 
    bool condition = innerAction(); 
    if (condition) 
    { 
     yield return e1; 
    } 
} 

запрос, которую затем будет что-то вроде

Func<TIn, TOut> selectFunction = e => e.name; 
foreach(var e2 in list2) 
{ 
    yield return selectFunction(e2); 
} 

Результаты будут переданы в отдельную функцию, которая является еще одним foreach циклом, и который будет принят к функции contains, что приведет к простому циклу foreach.

Так что ответ на ваш вопрос: «Да, они более или менее эквивалентны», но это связано с большим количеством лямбда :) И «да, это будет повторять Distinct на каждой итерации», потому что он вызывает сгенерированный лямбда снова. Вы бы лучше оценили innerAction из моего первого листинга на ToArray() или ToList() (в вашем случае назовите его внутренним действием условия).

1

LINQ (для объектов) по существу является основой методов расширения для построения комбинации функциональных выражений, которые получают прикладную последовательность, которая представлена ​​IEnumerable<T1> в библиотеке .Net. CLR не нужно знать об этом: для CLR LINQ есть только lambdas и методы расширения.

Что интересного для вашего вопроса, так это то, как поток выполнения во время запроса LINQ. В обоих случаях исполнение не выполняется вообще. Запрос LINQ не оценивается до тех пор, пока результирующий IEnumerable<T2> не будет повторен. Это также означает, что в обоих случаях оценивается запрос для разных имен.

Я думаю, что это невозможно предотвратить в синтаксисе метода расширения. Но возможно что-то вроде этого:

var distinctNames = list2.Select(o2 => o2.name).Distinct().ToArray(); 
var filteredlist = list1.Where(o => distinctNames.Contains(o.name)); 

Или вы могли бы попытаться использовать синтаксис запроса LINQ с let-statement. Но я думаю, что эти два заявления делают это яснее.

2

Ответ Маттен совершенно прав, однако я хотел бы отметить, что существует простой способ гарантировать, что Distinct не будет выполняться снова и снова. Все, что вам нужно сделать, это сначала ввести IEnumerable в фактические данные, т.е.:

var distinctNames = list2.Select(o2 => o2.name).Distinct().ToList(); 
var filteredlist = list1.Where(o => distinctNames.Contains(o.name)); 

И, конечно же, поиск в списке является линейной операцией, так что в худшем случае, вы делаете distinctNames.Length * list1.Length сравнения. Если вы используете словарь вместо:

var distinctNames = list2.GroupBy(i => i.name).ToDictionary(i => i.Key, i => true); 
var filteredList = list1.Where(i => distinctNames.ContainsKey(i.name)); 

вуаля, теперь поиск производится в чем-то ближе к п * журнала (п) времени :) Тем не менее, обратите внимание, что это имеет смысл только если list1 не значительно меньше, чем list2 - в этом случае построение словаря может в значительной степени компенсировать рост производительности позже. Всегда используйте правильный инструмент для задания: P

Смежные вопросы