LINQ (Language Integrated Query) is a great feature available to C# Unity developers. Many developers don’t know it exists or how to use it though, and lose out on the great time & code savings it can provide. LINQ in Unity has a variety of great uses, and a couple pitfalls you’ll want to avoid.
To use LINQ in a C# script, you need to add a using statement for the namespace like this.
What Can I do with LINQ?
The most common uses for LINQ statements tend to be sorting, searching, and filtering (though there’s plenty more you can do).
For example, let’s take a scenario where we want to find the closest game object to the player.
Compare that to the LINQ version
As you can see, LINQ has an OrderBy extension method for collections. Because we have a List of gameobjects, and List implements IEnumerable, we can use OrderBy to easily sort objects by distance, then take the first from the collection (since they’re in ascending order).
What the hell is that t => ????
You’ll see this a lot in LINQ statements, it’s called a lambda operator.
In the case of this OrderBy statement, think of the “t” as a reference to each object in the collection. When it orders them by distance, it’s passing in our local objects position, and the position of “t”.
Does it have to be “t”?
That “t” is just a variable name. It could just as easily be named “a”, “thingToCompare”, or any other variable name. “t” is just a common standard in lambda examples. Often, I’ll use a variable name that’s more descriptive, especially when building more complex LINQ statements.
The FirstOrDefault() call makes our LINQ statement return the first object from the collection… OR whatever the objects default value is. Default is just the value you’d have if you never assigned a value.. for objects that’s null.
There’s also a First() method, but that will throw an exception if there’s nothing in the collection. Sometimes you want this, but I find myself almost never using it.
What about Performance?
Of course with anything, you’ll need to consider the performance of operations like this. In the majority of cases using LINQ won’t hurt you at all. It may add a couple nanoseconds here or there, but it can also shave some time off if your custom code isn’t completely optimized.
The one thing you definitely do need to look out for though is Garbage Collection. LINQ statements will generate a little garbage, so avoid using them in something that’s going to be called every frame (don’t put them in your Update() calls).
For other events though, LINQ can be a huge time saver, make your code easier to read, and having less code always reduces the chance for bugs.
Multiple Lines or One Line?
When you look at LINQ statements, sometimes they’re written as a single long line. Sometimes there’s a line per method..
Functionally, it doesn’t make any difference. Personally, I prefer a line per method because it makes the call easier to read at a quick glance. So while there’s no set rule on it, I recommend you split your LINQ statements with a new line before each method call (with the period on the newline).
The Take method can be used to “Take” a subset of a collection and put them into another collection.
As an example, imagine a case where you want to find the 4 lowest health enemies. The Take method makes that easy.
Sorting by multiple things
You saw the OrderBy() method above, which is great for sorting, but sometimes you’ll need to sort by more than one thing.. For example imagine you have a scene full of Dogs… and you want to sort the dogs by Color and Size.
Switching The Order
If you want to sort in the opposite direction, you can use OrderByDescending() to reverse the order.
Often when you see a LINQ statement acting on a collection, you’ll see it end with ToList();
There’s a very important reason this is done.. and that reason is called deffered execution.
When you use a LINQ statement, the execution of the statement doesn’t happen until it’s needed.
Take a look at this example:
The ordering and distance checking of coins is only done if the player presses ‘A’. And even then, it’s not until the delay has passed and we’ve reached the foreach statement. If that’s never reached, the deferred call isn’t needed so it’s never run.
The downside to this is we have a bit less control over execution time. Sometimes that’s fine, other times we want to enforce execution immediately.
And to force that execution, we can call ToList() or ToArray().
What about the other syntax?
It’s important to note that there are two different types of LINQ syntax. There’s the one I’ve shown you so far, and another that looks a bit more like SQL. For Unity developers, I’d recommend staying with the fluent syntax you see here and avoid the SQL one. I’ve found developers who don’t do much SQL work tend to get a bit more confused by the other syntax and confusion causes bugs.
What other operators are there?
There are a TON of them. I’ve covered a couple of the most common ones, but I recommend you view the larger list here just to know what’s available:
Here are some of the ones I find myself using more often
- GroupBy() – groups things as you’d expect.. often end up using this with ToDictionary
- ToDictionary() – yep it builds a dictionary with the keys/values you want from any other collection(s).
- Any() – tell you if any object in the collection meets a criteria (returns true or false)
- Skip() – great for paging, often used with Take
- Contains() – easy way to check if a collection contains a specific object
LINQ is amazingly powerful, and with just a little time learning the syntax, it can be a huge time saver. It’s also important to be able to read in other people code.. Outside gaming, in other C# projects, you’ll see LINQ everywhere. As I mentioned above though, garbage collection and performance are extremely important for games, so you still need to think and profile when using it in your projects. That’s true for everything though, so don’t let it discourage you from taking advantage of this amazing language feature.
For more examples and LINQ statements, check out this site: http://linqsamples.com/