In the System.Linq
namespace, we find many extensions to the C# language. These enhancements include support for query expressions, and extension methods.
With query expressions, we can search or sort a collection with just a few keywords, directly in the C# program. We can even group together elements in a certain way.
To begin, this example introduces a query expression—it starts with the keyword "from" which is a contextual keyword. Query expressions often end in "select."
int
array. We keep all elements greater than or equal to 10 in the result.foreach
runs.using System; using System.Linq; int[] values = { 1, 10, 100, 5 }; // Step 1: select all elements greater than or equal to 10. var result = from v in values where v >= 10 select v; // Step 2: evaluate the query. foreach (var value in result) { Console.WriteLine(value); }10 100
One of the most useful features of LINQ is orderby
—this allows us to specify that a query sorts its results. Here we use the default int
sort, which goes from low to high.
orderby
clause, but it is the default, so this is not necessary.using System; using System.Linq; int[] values = { 3, 10, -1, 30, -3 }; // Sort in ascending order, from low to high. var result = from v in values orderby v select v; foreach (var value in result) { Console.WriteLine(value); }-3 -1 3 10 30
Sometimes we want a reverse-ordered sort—a descending instead of ascending sort. With LINQ, we can use orderby
with the descending keyword after it.
using System; using System.Linq; string[] animals = { "cat", "turtle", "ant" }; // Sort strings reverse alphabetically, from last to first. var result = from a in animals orderby a descending select a; foreach (var value in result) { Console.WriteLine(value); }turtle cat ant
We can use query expressions to group together the results of the query. We need to end the query with a "group" clauses, which includes a "by" part.
List.Add
5 times. Each tuple has 2 items: an int
, and a string
.Item1
) in each tuple.foreach
loop, we display the complete results within each group.using System; using System.Collections.Generic; using System.Linq; // Step 1: create list of tuples. var list = new List<Tuple<int, string>>(); list.Add(new Tuple<int, string>(5, "green")); list.Add(new Tuple<int, string>(5, "blue")); list.Add(new Tuple<int, string>(20, "orange")); list.Add(new Tuple<int, string>(20, "yellow")); list.Add(new Tuple<int, string>(20, "magenta")); // Step 2: use query to group by first item in each tuple. var result = from v in list group v by v.Item1; // Step 3: display results. foreach (var value in result) { Console.WriteLine(value.Key); foreach (var item in value) { Console.WriteLine(" " + item.Item2); } }5 green blue 20 orange yellow magenta
When we include the System.Linq
namespace, we gain access to many extension methods that act upon IEnumerables
. The Average()
method is one such extension.
IEnumerable
is returned by a query expression, and things like arrays and Lists are also IEnumerables
.using System; using System.Linq; int[] numbers = { 2, 4, 6, 8 }; // Use Average extension method. var average = numbers.Average(); Console.WriteLine(average);5
It is possible to call an extension method right after another one—in this way, we can "chain" extension methods. Here we use Take
, and then Average the result of Take
.
using System; using System.Linq; int[] elements = { 5, 10, 15, 20, 25 }; // Average first 3 numbers. var averageFirst3 = elements.Take(3).Average(); Console.WriteLine(averageFirst3);10
The Any()
method is an extension—we must include System.Linq
to access it. It receives a lambda expression (a Predicate
) that must return a bool
value.
IEnumerable
, the Any method returns true.using System; using System.Linq; int[] values = { 10, -200, 1, 30, -1 }; // Use Any() to determine if a negative number exists. var hasNegative = values.Any(x => x < 0); Console.WriteLine(hasNegative);True
We can use an extension method directly upon a query expression—we need to surround the expression in parentheses. Here we Take()
the first 2 results of an orderby
query.
using System; using System.Linq; int[] numbers = { 5, 10, 1 }; // Use Take directly on a query. var result = (from n in numbers orderby n select n).Take(2); foreach (var value in result) { Console.WriteLine(value); }1 5
FirstOrDefault
With this method, we get the first element in an IEnumerable
, if one exists. If the collection if empty, we get the default value—for int
, this is 0.
using System; using System.Linq; int[] elements = { 5, 10, 15, 20, 25 }; // Part 1: use FirstOrDefault when an element exists. var first1 = elements.FirstOrDefault(); Console.WriteLine("FIRSTORDEFAULT: {0}", first1); elements = new int[] {}; // Part 2: use FirstOrDefault on empty collection. var first2 = elements.FirstOrDefault(); Console.WriteLine("FIRSTORDEFAULT: {0}", first2);FIRSTORDEFAULT: 5 FIRSTORDEFAULT: 0
ElementAtOrDefault
Similar to the FirstOrDefault
method, ElementAtOrDefault
allows us to safely get an element at any index. If we pass an out-of-range index, the default value is returned.
ElementAt
method is shown. This extension method will throw an extension if the index is out-of-range.using System; using System.Linq; string[] sizes = { "small", "medium", "large" }; // Use ElementAtOrDefault with various indexes. var element1 = sizes.ElementAtOrDefault(1); Console.WriteLine("ELEMENTATORDEFAULT: {0}", element1); var element100 = sizes.ElementAtOrDefault(100); Console.WriteLine("ELEMENTATORDEFAULT: {0}", element100); // Use ElementAt, which may cause an error. var element2 = sizes.ElementAt(2); Console.WriteLine("ELEMENTAT: {0}", element2);ELEMENTATORDEFAULT: medium ELEMENTATORDEFAULT: ELEMENTAT: large
LINQ is a powerful feature in the C# language, and its many extension methods can often simplify programs. And query expressions often help with sorting elements within a collection.