Что на самом деле происходит с IQueryable.Where ()? - PullRequest
2 голосов
/ 18 августа 2010

Возвращает логическое значение в зависимости от наличия соответствующих идентификаторов.

from t in getAll
select new Result
{
...             
    bool DetailsAvailable = 
        (db.SaveTrackings.Where(s => s.BundleID == t.bundleID 
                                  && s.UserID == t.userID)
                         .Count() > 0) ? true : false;
}

Вот что я понимаю: .Where() возвращает все записи с совпадающими идентификаторами, а затем .Count() просто видит, сколько их там. Я только чувствую, что наполовину понимаю, для чего нам нужно s.

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

Все лямбда-выражения используют лямбду оператор =>, который читается как "идет к ". Левая сторона лямбды Оператор определяет ввод параметры (если есть) и правая сторона содержит выражение или утверждение блок. Лямбда-выражение x => x * х читается "х идет х раз х".

Итак, как мне понять, что означает мой код, основываясь на этом, .Where(s "переходит к" s.BundleID == t.BundleID ...), так что же здесь происходит? Что значит "идет"? Сравнивает ли каждый идентификатор в s с каждым, доступным в t? Как я понимаю, почему это называется «идет» и что именно происходит?

А потом становится все более запутанным ...

Оператор => имеет тот же приоритет как присваивание (=) и является правоассоциативная.

Лямбды используются в LINQ на основе методов запросы в качестве аргументов для стандартного запроса методы оператора, такие как Where.

Когда вы используете основанный на методе синтаксис для вызвать метод Where в Перечислимый класс (как вы делаете в LINQ для Объекты и LINQ to XML) параметр тип делегата System.Func. Лямбда-выражение является самый удобный способ создать это делегировать.

Что такое тип делегата System.Func<T, TResult> и как он создается с помощью этого оператора «идет»?

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

Ответы [ 3 ]

2 голосов
/ 18 августа 2010

Может быть, это поможет увидеть, как эта функция реализована вручную:

using System;
using System.Collections.Generic;

namespace CSharpSandbox
{
    class Program
    {
        static IEnumerable<T> Where<T>(IEnumerable<T> input, Func<T, bool> predicate)
        {
            foreach (T item in input)
            {
                if (predicate(item))
                    yield return item;
            }
        }

        static void Main(string[] args)
        {
            int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
            IEnumerable<int> evens = Where(numbers, n => n % 2 == 0);
            foreach (int even in evens)
            {
                Console.WriteLine(even);
            }
        }
    }
}

Конструкция name => someEvaluation создает анонимную функцию, состоящую из следующих частей:

  • name - это просто имя параметра, его тип определяется из его использования. Вам нужно имя, чтобы вы могли ссылаться на аргумент, переданный в функции.
  • => - это начало вашего тела анонимных функций, область видимости тела - это одно выражение.
  • someEvaluation - это тело вашей анонимной функции, состоящее из одного выражения.

В нашем случае Func<T, bool> определяет функцию, которая принимает один параметр типа T и возвращает вывод типа bool. (Если бы мы использовали Func<T, U, bool>, мы бы взяли два входа типа T и U и вернули бы bool. Последний параметр типа в определении Func является возвращаемым значением.)

Вы можете вызвать экземпляр Func точно так же, как вы вызываете любую другую функцию. Если функция принимает параметры, вы передаете их как положено, ваши параметры привязываются к именам переменных, которые вы определили. Когда вы вызываете функцию, поток управления переходит в вашу функцию и оценивает ее результаты.

В принципе вам не нужно создавать Func анонимно. Вы можете передать любую функцию, имеющую совместимую сигнатуру типа, например:

    static bool IsEven(int n)
    {
        return n % 2 == 0;
    }

    static void Main(string[] args)
    {
        int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        IEnumerable<int> evens = Where(numbers, IsEven);
        foreach (int even in evens)
        {
            Console.WriteLine(even);
        }
    }

Эта программа выдает тот же результат. Фактически, за кулисами синтаксис name => expression является синтаксическим сахаром; после компиляции C # создаст приватную функцию со скрытым именем и преобразует ее в приведенный выше формат.

2 голосов
/ 18 августа 2010

Если это поможет, подумайте о s как о переменной типа SaveTracking.Он перебирает каждый s в вашей коллекции / таблице и проверяет значение BundleID.

Идея t такая же - она ​​как бы перебирает всю коллекцию возврата из getAll.

Это как псевдокод SQL:

 SELECT * FROM SaveTracking INNER JOIN GetAll 
    ON BundleID AND UserID

Более подробное техническое описание того, что происходит с лямбда-выражениями, можно найти в книге Джона Скита C # In Depth .Глава 9, стр. 230. Я нашел эту книгу очень полезной.

0 голосов
/ 18 августа 2010

Лямбда-выражения - это просто способ сократить код, но он делает то же самое, что и объявление метода, соответствующего типу делегата System.Func<T, TResult>

Я считаю, что C # преобразует вашу лямбу в метод в фоновом режиме, когда вы компилируете, и это выглядит так:

bool LambdaExpression(YourType s)
{
   return  s.BundleID == t.bundleID && s.UserID == t.userID;
}
...