Where
is probably implemented like this (note that this is a very rough implementation, just do give you an idea of what it is like):
public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, bool> predicate) {
foreach (T t in source) {
if (predicate(t)) {
yield return t;
}
}
}
Note that Where
is an extension method. You are essentially calling Where(customerList, c => c.Age > 30)
. Now you should see where it gets access of the customerList
. It also infers what T
should be by looking at what kind of IEnumerable
customerList
is. Since it is a IEnumerable<Customer>
, T
is Customer
, and so it expects a Func<Customer, bool>
for the second argument. This is how it knows that c
is Customer
.
Where
does not return a temporary list. A lot of LINQ methods make use of deferred evaluation. Where
returns an IEnumerable<Customer>
that is filtered. Remember that IEnumerable
is just a sequence of things. This sequence returned by Where
however, is not evaluated, until you ask for it. Select
does the same as well. So a list is not created until you call ToList
.
Your third question is kind of like asking "How does Where
know how to filter" or "How does Console.WriteLine
know how to write to the screen". That's what Select
does. You can look at its implementation if you want. Here's a rough sketch:
public static IEnumerable<U> Select<T, U>(this IEnumerable<T> source, Func<T, U> mapping) {
foreach (T t in source) {
yield mapping(t);
}
}
The type of the variable salaries
is determined by looking at the method signatures of each of the methods you call. Your Select
call returns a IEnumerable<long>
, and ToList
's signature tells the compiler that, given a IEnumerable<T>
it will return List<T>
, so in this case, it returns a List<long>
.