There was a time when Linq was a mystery to me. But, now that I’ve learned how to use it, I don’t know how I ever lived without it! You’ll learn Linq with this complete beginner’s gentle introduction. All you need to know before you start is how to code a loop. Heck, I’ll even show you that first. Ready? Let’s get started!
Start With a Loop
Let’s say you need to count elements in an array. Your count method may look like this:
1 2 3 4 5 6 |
int countOfNumbers = 0; foreach(var number in numbers) { countOfNumbers++; } return countOfNumbers; |
The first step is to create a variable initialized to 0. Then, we iterate through the array of numbers. For each iteration, we increment the count by one, using “++”.
Make It Reusable
Sure, we could’ve just returned “.Length” of the array like this:
1 |
return numbers.Length; |
But what if we want to get the count of other types of collections too? List, Dictionary, ArrayList, and many other collection types implement the IEnumerable interface. An IEnumerable is simply a promise to return an enumerator. An enumerator lets the programmer enumerate (loop) through the collection one item at a time.
By depending on an IEnumerable<T> rather than an array, our method can be reused. We can pass it an array, a list, a dictionary, or anything else that implements IEnumerable<T>.
Let’s make a “Count” method that can be reused:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public static int Count(IEnumerable items) { int count = 0; ver iter = items.GetEnumerator(); while(iter.MoveNext()) { count++; } return count; } // or the shorter version public static int Count(IEnumerable items) { int c = 0; foreach(var i in items) c++; return c; } |
As you can see, we can create a method to count items so we don’t have to write out the whole loop every time. Check out this fiddle to see how it works. You can alter the fiddle to try different things, and the original fiddle will stay intact.
Turn It Into an Extension Method
Linq is simply a collection of extension methods. They extend certain interfaces and types. You can use the following pattern to make your own extension method:
1 2 3 4 5 6 7 |
public static <ClassName> { public static <typeToReturn> <MethodName>(this <typeToExtend> <paramName>, ...) { ... } } |
Notice that the class and the method are both public and static. And the key to extensions is the fact that the first argument is a “this arg”, meaning it extends the type of the first argument. Note the use of the “this” keyword in the argument.
Employing this template on the Count method, we get the following:
1 2 3 4 5 6 7 8 9 |
public static class MyExtensions { public static int Count(this IEnumerable items) { int c = 0; foreach(var i in items) c++; return c; } } |
As you can see, the method is the same as the previous “Count” except for “this” is now part of the first parameter. This method is on a static class named “MyExtensions”.
Now, let’s see how we would use this extension method.
Using an Extension Method
Since Linq methods are extension methods, you’ll need to know how to use one. I made another fiddle to show you how. In that fiddle, I simply called the method as if it existed on the IEnumerable instance. It looks like this:
1 |
int c = items.Count(); |
Using an extension method is that simple. Your instance has to be the same type as the “this” argument. The instance can be the same concrete type, a subtype, or an implementation of the interface.
Now that we’ve seen extension methods, we have a good base for understanding Linq extensions. Let’s see what we can do with them.
Using Linq Extensions
Linq extensions are just like the “Count” extension we just made. In fact, Linq has a “Count” method, and it’s used exactly the same way as ours!
There are many other simple Linq methods. Some of my favorites are “Any”, “First”, and “FirstOrDefault”. Those are handy for working with collections. Next, we’ll cover one of my favorites in action.
Any
“Any” is great because it makes your code more concise. And concise is good! For example, let’s say you need to do something if you have at least one number in your array. The long-form way is to create a loop. One shortcut is to use “Count” from Linq and compare the result like this:
1 |
if(numbers.Count() >= 1) doSomethingWithNumbers(numbers); |
I’m sorry for the nebulous example, but it’s meant to show you how to use “Count” in a logical expression. First, count the numbers. Next, compare the result to 1. Finally, if the count is greater than or equal to 1, do something.
Another way to say “at least 1” is to say “Any”. We can find out if there are any elements in the numbers collection using the following code:
1 |
if(numbers.Any()) doSomethingWithNumbers(numbers); |
See how that’s more concise? “Any” returns a Boolean value. If there are any numbers, then do something. It’s much more efficient to read and write this logic with “Any”. You don’t have to poll the comparison part of your brain to understand the meaning. It’s plain language.
The next topic is a bit more challenging but really important to understand how to use Linq.
Intro to Lambda
In Linq, a lambda operates on each element in the collection. The format for a lambda is the following:
1 |
arguments => statement |
A lambda can have one or more arguments. Think of an “arrow” pointing to the statement. The statement can be arbitrarily complex. There are some restrictions on the statement, but it must return the expected type. You can also think of a lambda as a method in a different format.
Here are four examples of how to write various lambda formats:
1. No input, void return, operate on a variable outside the scope—when passing nothing into a lambda, you must use parentheses.
1 2 3 |
// return type is void int i; () => i++; |
2. Single input, direct return—you don’t need parentheses or curly braces when there’s one argument and a direct return.
1 2 |
// return an int x => 1; |
3. Single input in parentheses, full return statement—you can use parentheses and curly braces.
1 2 |
// or in long-form (x) => { return 1; } |
4. Multiple inputs, full multiline return statement—a lambda can be complex. You can create variables within the scope of the statement.
1 2 3 4 5 6 7 |
// multiple arguments, SaveUser result type (id, name, rank) => { var user = userRepository.Get(id); user.UpdateName(name); user.UpdateRank(rank); return user.Save(); } |
These four examples cover the various formats for lambdas.
Lambdas in Linq
We use lambdas in Linq to map and filter. When we use it to map, or to return a new object or value, it’s a selector. When we use a lambda as a filter, it’s a predicate. A predicate returns a Boolean value. For example:
1 |
numbers.Any(number => number > 0); |
This predicate will return true when the number is positive. When the number is not positive, it’ll return false. “Any” will return true if there’s a single positive number in “numbers”. Or, it will return false.
Beyond Primitives
“Any” is useful on collections of primitives, but you can also use it on collections of complex types. The following example calls a method on each object that evaluates to a Boolean:
1 |
if(users.Any(user => user.HasPermission(subject)) takeAction(users); |
In this example, I’m checking through the list of users to see if any have permissions to the subject. If so, I’ll call the “takeAction” method. This example illustrates a potential danger of using Linq. Imagine if “user.HasPermission” makes a database or API call. In that case, we’d end up making a call for each user in the collection of users. That could bog down the system quite a bit! One benefit of “Any” over “Count” is that it stops when it finds a match.
Selector in Select
The “Select” method takes a selector. You can use it to map collections in this way:
1 |
var names = users.Select(user => $"{user.FamilyName}, {user.GivenName}"); |
In this example, I’m plucking off the name values into a string interpolation to create a full name. This creates a new collection with the results.
But take caution: “names” won’t actually point to the collection. Instead, it will point to an enumerator. You see, many of the Linq extensions return either an IEnumerable or an IQueryable, depending on which type you called it on. In this case, if I never actually iterate “names” after this map, the selector will never enumerate the original collection!
You’ve Got It From Here!
Linq is nothing more than a way to wrap iterators. It’s a collection of extension methods on the IEnumerable<T> and IQueryable<T> types. Extension methods are methods that you can “add” to a type. You can use Linq extensions to simplify your code. Most Linq methods can take a predicate as an argument. A predicate is a function delegate that operates on each item in the collection. Linq methods return collections, objects, and values, depending on the method.
I should mention that extensions on IQueryable<T> are usually associated with database queries. Database adapters for Linq translate the methods into queries that run on the database. There are several types of adapters, and it can get a bit more complicated than basic Linq. I kept away from this topic for now so we can focus on Linq by itself without the extra complexity.
For now, give the steps above a try and get to know the other Linq methods. They’re great tools to have in your repertoire!
Thanks, Nevada! I enjoyed writing this post and sharing my knowledge and experience. I’m glad you find it useful!