The Magic of LINQ Expressions
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<Person,string>(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!
#1 by Yohan on September 7th, 2015 ·
Hey there!
Looks interesting, thanks for sharing.
But, what is “p” in the code: “p => p.First”?
I can’t get it to work on a small example…
Is it an instance of the Person class?
Thanks!
#2 by jackson on September 7th, 2015 ·
If you were to call it,
p
would be an instance of thePerson
class. In the example, it’s never actually called as that’s not required to get the field name.What problem are you running into trying to run the example?
#3 by Yohan on September 8th, 2015 ·
Well, in Unity I have this:
class Person
{
public string First;
public string Last;
public int Age;
}
public static string GetFieldName(Expression<Func> expression)
{
return ((MemberExpression)expression.Body).Member.Name;
}
void Start()
{
var fieldName = GetFieldName(p => p.First);
Debug.Log(fieldName); // prints: First
}
And I have the error message:
“error CS0411: The type arguments for method `NewBehaviourScript.GetFieldName(System.Linq.Expressions.Expression<System.Func>)’ cannot be inferred from the usage. Try specifying the type arguments explicitly”
And in my IDE, the “First” in “p.First” is red, how does it even know what p is? I don’t get it.
Thanks :)
#4 by jackson on September 8th, 2015 ·
Your comment just made me realize that the snippet from the article that you tried to use was incorrect because it was missing the type parameters. I’ve updated the article with the correct line:
In this case, the compiler can’t determine the type arguments automatically so you need to specify them. First you specify
Person
because theFunc
lambda takes one of them and then you specifystring
because the lambda returns one of them.Hopefully that clears things up for you. Thanks for reporting the issue!
#5 by Yohan on September 8th, 2015 ·
Yes now it’s clear and working, thanks for taking the time :)
#6 by Joe on June 14th, 2017 ·
Alternatively,
public static string GetFieldName(Expression<Func> expression)
{
return ((MemberExpression)expression.Body).Member.Name;
}
var fieldName = GetFieldName(p=>p.First);
#7 by Joe on June 14th, 2017 ·
And again, encoding those braces…
Alternatively,
public static string GetFieldName<T>(Expression<Func<T, dynamic>> expression)
{
return ((MemberExpression)expression.Body).Member.Name;
}
var fieldName = GetFieldName(p=>p.First);
#8 by Joe on June 14th, 2017 ·
Last attempt. The code I can do; getting onto your site I’m struggling with.
The last line above should, of course, also have the type specified:
var fieldName = GetFieldName<Person>(p=>p.First);
#9 by jackson on June 14th, 2017 ·
I get a compiler error on the
GetFieldName
call with Unity 5.6.1f1:Have you gotten this to work in Unity?