Visual Studio How-to: LINQ - ' A more useful example' (
Page 2 of 3 )
A slightly more useful example
Next let's look at an example that's just a bit more useful than looking for particular integers. You can add this function to the same program and then change the Main function to call this one instead. You'll also need to add a line at the top, using System.Diagnostics.
public static void Linq2()
{
// Need to add using System.Diagnostics; to top of program
var processes =
from p in Process.GetProcesses()
where p.ProcessName.Contains("ms")
select p;
foreach (Process x in processes)
{
Console.WriteLine(x.ProcessName);
}
}
ADVERTISEMENT
This will search all the processes whose name contains the string ms. Notice how I set up the query: I have a from clause specifying the collection of process names returned by Process.GetProcesses. Then I have a where clause giving the criteria. And fnally I have the select clause.
If you typed this code in (instead of just pasting it from this web page), you might have noticed something interesting. Visual Studio's Intellisense (that thing that pops up and shows you the members of your variables, for example) is incredibly smart. Look at this:
When I started typing my where clause, Intellisense kicked in. But look closely: Intellisense knew the type of p. In order to determine the type of p, Intellisense had to parse the second half of the from clause, and determine that Process.GetProcesses() returns a collection of Process types, and next, knowing that p is an individual element in the collection, determine that p's type is Process. Whew!
A quick note about functional programming and Lambda Expressions
The where clause of a query consists of an expression. This expression can be as complex as you want it to be. But when is the expression actually evaluated? It's evaluated when the runtime needs to test the values of the set to determine which ones fit into the resulting set. In other words, this expression is a miniature little function, and each member of the original set is passed as a parameter into the function—even though the where clause doesn't actually look like a function.
Really, you could manually scan through the data set yourself, and provide a separate function that tests individual members, calling the function over and over for each member in the original set. But having such an external function can start to clog up your code, adding more and more lines to what might already be a large program. Instead, you can use this compact form of a simple, inline function.
Under the hood, C# is creating an anonymous function (meaning it has no name) in the form of a lambda function. A lambda function is a new aspect of C# but not something new to the programming world. A lambda function, in this case, is a special way of declaring a function in an extremely compact way. You can use lambda expressions where you see fit, not just in LINQ code. Here's a very short example of a lambda expression:
Func<int, bool> test = x => x > 10;
Console.WriteLine(test.Invoke(15));
The first line declares the lambda expression. The format is
function parameter => function expression
The variable called test stores the lambda expression. The lambda expression in this case is x => x > 10. That's really just a function that takes x as a parameter and test if the parameter is greater than 10. In declaring the lambda expression, we use a template-like syntax of Func<int, bool>. The first parameter, int, is the type of the function parameter (x is an integer), and the second paramter, bool, is the type of the function's return value (x > 10 returns a bool).
When using where clauses, you only specify the right half of the lambda expression, which is the expression itself (such as x > 10).
That's just a very quick into to lambdas. For more information see our Cigars, Lambda Expressions, and .NET, by Paul Kimmel.
Creating new objects in the query
The select clause might seem unnecessary at first. In the previous examples, the "select x" business would probably seem obvious; it seems that the code
from n in numbers
where n > 25
should be all you need. But the select clause does serve a purpose; in it you can create new objects based on those objects that fit the criteria in the where clause.
Have a look at this code, which is a slightly modified version of the previous:
public static void Linq3()
{
var processes =
from p in Process.GetProcesses()
where p.ProcessName.Contains("ms")
select new { p.ProcessName, p.Id };
foreach (var x in processes)
{
Console.WriteLine(x.ProcessName);
Console.WriteLine(x.Id);
}
}
Look carefully at the select clause. In it, I'm creating a set of new objects, each with a ProcessName member and an Id member. These new objects don't have a class name; I'm creating an anonymous type. The new statement is called for each item in the resulting set, and the members of the anonymous object are initialized with the values in p.ProcessName and p.Id. (The names of the membvers of the anonymous class are inherited from the original object; thus the names are ProcessName and Id.)
Now look at the foreach statement. The members of the collection are no longer instances of Process, so I couldn't declare the loop variable as Process. Instead I just used var to let the compiler figure it out for me. (Remember, the type is anonymous, so it doesn't have a direct name!)
The anonymous types you create actually have kind of a cool feature; they automatically get a ToString() method. You could code your foreach statement like so:
foreach (var x in processes)
{
Console.WriteLine(x.ToString());
}
The output will look like this (depending on what processes you have running matching the criteria):
{ ProcessName = smss, Id = 796 }
{ ProcessName = ibmpmsvc, Id = 1136 }
{ ProcessName = msmsgs, Id = 648 }
Visual Studio is smart once again in its Intellisense; check out the following screen capture. Intellisense detected the anonymous types in the foreach statement and automatically popped up the list of members. (I imagine somebody at Microsoft spent a lot of late nights working on that one!)