LINQ expressions aren’t the same thing as LINQ queries. They’re more like reflection for the syntax of C# itself. It’s a fascinating—and powerful—area of the language and I’ll be exploring it a little in today’s article.

The core class we’re talking about today is System.Linq.Expressions.Expression. It’s the base class of a whole bunch of SomethingExpression classes, where Something can represent lots of code you can write.

You see, the Expression represents code like a Type represents a class. It’s not an evaluation of that code, but rather the reflection of it. This means you use an Expression to inspect other code you’ve written.

“What could this possibly be useful for?”, you may ask. Well, once you have the tool you start to think up better solutions to all kinds of problems.

One simple one that comes to mind is the problem of needing the user of a function to pass in a string parameter with the name of a field. You’d probably use reflection to find that field using the string name later. But this is problematic because the string is not validated by the compiler. So if you make a typo or you change the name of the field then your code will break at runtime, which is less than ideal.

Enter LINQ expressions. You can use them to move the error from run time to compile time. You just have to think up a way to express a field name in C#. There are lots of ways to do that, but it turns out that a convenient one is to use a lambda:

// Class to get field names from
class Person
{
	public string First;
	public string Last;
	public int Age;
}
 
// Lambda expressing the field name: First
p => p.First

One big advantage is that you don’t need an actual instance of a Person to express its field name with a lambda. It’s just like a function that hasn’t been called. It’s kind of like how you can get the type of a class using typeof(Person) without actually instantiating one.

Now how do we use LINQ expressions to get the field name? It’s actually really easy! You just wrap the thing you want with Expression<T>. In this case, we have a lambda that takes a Person and returns a string, otherwise known as a Func<Person,string>. That means we need an Expression<Func<Person,string>>:

Expression<Func<Person,string>> expression = p => p.First;

Now that we have an Expression, we can inspect it. Since this is an expression for a lambda, we actually have a LambdaExpression. That exposes the two parts of a lambda: Parameters and Body. The body is where we put the field name, so let’s grab that:

Expression<Func<Person,string>> expression = p => p.First;
Expression body = expression.Body;

Now what type of expression is the body of the lambda? Well, it refers to a member of the class: First. That makes it a MemberExpression, so let’s cast to that:

Expression<Func<Person,string>> expression = p => p.First;
MemberExpression body = (MemberExpression)expression.Body;

MemberExpression has a Member property with information about the member being accessed. It’s a MemberInfo just like you’d get from typeof(Person).GetMember("First"). So let’s get that property:

Expression<Func<Person,string>> expression = p => p.First;
MemberExpression body = (MemberExpression)expression.Body;
MemberInfo info = body.Member;

Finally, let’s get the string name of the member from the MemberInfo:

Expression<Func<Person,string>> expression = p => p.First;
MemberExpression body = (MemberExpression)expression.Body;
MemberInfo info = body.Member;
string fieldName = info.Name;

This shows a type-safe way to get field names. No more typos! No more forgetting to update strings when field names change! But it’d be really tedious to type out all that code every time you want a field name, so let’s make a helper function:

public static string GetFieldName<T,K>(Expression<Func<T,K>> expression)
{
	return ((MemberExpression)expression.Body).Member.Name;
}

This helper function uses type arguments (a.k.a. generics) to allow for any class T and any field type K to be used. Using it is easy:

var fieldName = GetFieldName&lt;Person,string&gt;(p => p.First);
Debug.Log(fieldName); // prints: First

So far we’ve just scratched the surface of LINQ expressions. We only inspected a very simple lambda, but they expose far more than just that. An Expression forms a tree of sub-expressions that you can delve into for whatever purposes you have. For example, consider this lambda:

p => p.First == "Jackson" && p.Last == "Dunstan" && p.Age < 100

The Expression you get for that looks like this:

LambdaExpression
{
	Body = BinaryExpression
	{
		NodeType = AndAlso,
		Left = BinaryExpression
		{
			NodeType = AndAlso,
			Left = BinaryExpression
			{
				NodeType = Equal,
				Left = MemberExpression
				{
					Member.Name = "First"
				},
				Right = ConstantExpression
				{
					Value = "Jackson"
				}
			},
			Right = BinaryExpression
			{
				NodeType = Equal,
				Left = MemberExpression
				{
					Member.Name = "Last"
				},
				Right = ConstantExpression
				{
					Value = "Dunstan"
				}
			}
		},
		Right = BinaryExpression
		{
			NodeType = LessThan,
			Left = MemberExpression
			{
				Member.Name = Age
			},
			Right = ConstantExpression
			{
				Value = 100
			}
		}
	}

For a really good example of how to use LINQ queries both to get field names and to evaluate expressions like these, check out the open source single-file database system LiteDB. Fields are indexed using LINQ expressions like GetFieldName and the database is queried using LINQ expressions like the above tree. Unlike SQL queries, this is all type safe and checked by the compiler!

Lastly, I should note about IL2CPP compatibility. In the testing that I’ve done, LINQ expressions seem to be well supported on iOS with IL2CPP enabled. There is, however, another side to LINQ queries that I haven’t discussed today. That side is to build your own Expression instances, not from code but dynamically. For example, you can Compile() a LambdaExpression to a Delegate and then execute it with DynamicInvoke(). Dynamic code generation like this does not work with IL2CPP on iOS. However, as long as you use LINQ expressions in a sort of “read only” mode, you should be fine. Really, it’s just like using System.Reflection while avoiding System.Reflection.Emit.

That’s all for today’s coverage of LINQ expressions. If you’ve used them for anything, post about your experience with them in the comments!