This post is a continuation of a series in which I try to address the issue with people new to Unity who don't get to know C# and .NET good enough. In the last post I went over higher order functions and method delegates. In this post I will look into parts of the .NET library and methods that will help you write cleaner code.


Learn how to use Linq Expressions

Linq expressions (not to be confused with the Linq language also included in C# and a precursor to the Linq extension methods) are a collection of higher order function made to help you operate on collections found in the

System.Linq
namespace.


If you're operating on any sort of collection and wants to do any kind of operations on them, Linq can help you make the code neater and easier to read. Most of the boilerplate for iterating over lists and applying transformations are removed to a concise expression with only the transformation written out.


Example to find players who are alive

Assume the following state in our code. Now we want to find any player that is alive.

public class Player {
   public string Name;
   public string Score;
   public int Health;
}

var players = GetPlayers(); // Get a player list from someplace


For each implementation

var playersAlive = new List<Player>();
foreach(var player in players){
   if(player.Health > 0){
      playersAlive.Add(player);
   }
}


Linq implementation

var playersAlive = players.Where(p => p.Health > 0).ToList();

Short and concise. We are even doing two method calls here. The last ToList() calls is there because .Where() returns an IEnumarble and we wanted a list.


A simple mathematical operation


For each implementation of getting the total score.

var totalScore = 0;
foreach(var player in players){
   totalScore += player.Score;
}


Linq implementation

var totalScore = players.Sum(p => p.Score);


Multiple operations

Imagine if we want to do several operations at once? We want a list of the names of the top 5 scoring players who are still alive?


For each implementation.

var playersAlive = new List<Player>();

foreach(var player in players){
  if(player.Health > 0){
     playersAlive.Add(player);
  }
}

// Could be done with an implementation of
// IComparable for Player instead.
playersAlive.Sort(delegate(Player x, Player y)
{
   return x.Score - y.Score;
});

var topPlayerNames = new List<string>();
for(int i = 0; i < Math.Min(5, playersAlive.Count); i ++){
   topPlayerNames.Add(playersAlive[i].Name);
}

I have to be honest here, this kind of code is not unusual, specially for new programmers. It's also kind of long and not very clean. Try to read this in 10 seconds and try to grasp what it does and change it with confidence you got it right.


Linq implementation

var topPlayerNames = player
   .Where(p => p.Health > 0)
   .OrderBy(p => p.Score)
   .Take(5)
   .Select(p => p.Name)
   .ToList();

Cleaner and more concise. The code consists of five very specific and steps that are easy to read.


Lets go over the operations invoked here and explain what they do.


Where()

Applies a filter function to a list and returns the new list. Useful when you need a subset of an existing list.


OrderBy()

Orders the list asendingly based on a criteria. Uses the internal

IComparable
for whatever field you send it. You can also use
OrderByDescending()
if you want the list sorted the other way.


Take()

Returns a new list with the first X elements from the list. If the target list is of equal or shorter length than the requested amount it returns the whole list.


Select()

Usually called a Map in other languages. Takes a list and invokes a transform function for each element. In our case we took a list of Players and transformed it by only taking the name from it.


ToList()

Take the current list (actually IEnumerable in this case) and puts all the elements to a new list. This is useful because most Linq expression operates on and returns IEnumerables.


Some other useful Linq extension method

SelectMany()

Also called a flat map. Takes a collection of collections and returns a single collection.

Lets use an example.

public class Guild {
   public List<Player> Players;
}

var guild = GetGuilds();
var allMembersFromAllGuilds = guild.SelectMany(g => g.Players);

Here we have a list of guilds and they in turn with a list of Players. SelectMany returns a list of all players in all of the guilds as a single list.


Distinct()

Returns a new list with only unique elements.

Our previous example assumed a player can only be a member of a single guild but imagine if they can join multiple. The returned list could now have a single player multiple times. Lets extend it a bit.

var allMembersFromAllGuilds = guild.SelectMany(g => g.Players).Distinct();


Example from a Unity Editor

I've used Linq in many of my custom editors.

The following section is for a data editor I've made. The AssetDatabase static class has lots of helper methods to find / lookup assets but often they need to be chained. Instead of chaining every asset for themselves, we can just chain a bunch of Select() statements.

[System.Serializable]
public class Asset
{
   public Object Object;
   public string Name;
   public string Path;
   public Texture2D PreviewTexture;

   public Asset(string path)
   {
      Path = path;
      Name = path.Split('/').Last().Split('.').First();
   }
}

public List<Asset> FindScriptableObjectOfType(System.Type type)
{
  return AssetDatabase.FindAssets(string.Format("t:{0}", type.FullName))
     .Select(g => AssetDatabase.GUIDToAssetPath(g))
     .Select(p => new Asset(p))
     .ToList();
}