6

I'm not sure why I'm reading up on Expression Trees but I am. As such, I have no understanding of the object, or where or how it's used.

Reading other questions here, such as What are Expression Trees and how do you use them and why would you use them? (and the links they cite), I now understand the Expression Tree a little bit better but, I'm still lost as to why it is used.

The only example I've seen as to why, is things like Linq to SQL - but when you investigate it a little more, it appears to actually be more about it's use with IQueryable<T> interface.

Are expression trees only useful (used) in conjunction with IQueryable interface?

Community
  • 1
  • 1
MyDaftQuestions
  • 4,487
  • 17
  • 63
  • 120
  • 5
    It is a Lego block, might come in handy some day when you build your own death star. Microsoft needed it to implement Linq and the *dynamic* keyword. Just keep it in the back of your mind, some day you might also have a need to dynamically generate code. You've got System.CodeDom, the high-level approach, and Reflection.Emit, the low-level approach, expression trees are the intermediary solution. – Hans Passant Sep 28 '15 at 07:47

3 Answers3

8

It's a bit of a tautology, but the answer is "Whenever you need to understand an expression".

A common question I have seen from junior developers is "What is the difference between Func<T> and Expression<Func<T>>?" For most, the latter requires you call Compile() on the expression to get the function it represents, but otherwise they appear to do the same job.

Expression trees let the compiler embed what would normally be instructions as an explorable tree of expressions. If you are writing something that needs to understand not just the result of a function, but how a caller constructed their function, expression trees can capture this for you.

You have to need to know how the expression is composed before the difference becomes relevant.

I've only seen expressions used in two ways:

  1. Creating a LINQ provider (like LINQ-to-SQL) to interpret an expression and convert it into another language. As you already know, this involves implementing IQueryable and is a Big Deal™.
  2. Capturing property accessors from objects (like Razor HTML Helpers) so the value can not only be accessed, but also the name of the property can be used to generate things (like HTML field names).

The common task tends to be some sort of translation to another language. In the case of LINQ providers, it's a direct translation of a query into another query language. In the case of Razor, it's conversion of a property into an HTML field.

Paul Turner
  • 38,949
  • 15
  • 102
  • 166
4

Expression trees are often used to generate code dynamically at runtime. Lets say that you have to create lots of unknown type insatnces. You could create them using reflection and you'll suffer from poor performance, or you can create an expression tree and compile it to a method. The performance of that method would be the same as if you were compiling it using visual studio.

public static class TypeFactory
{
    private static Dictionary<Type, object> dictionary = new Dictionary<Type, object>();

    public static Func<TType> CreateFactory<TType>() {
        object factory;
        var typeofType = typeof(TType);

        if (dictionary.TryGetValue(typeofType, out factory)) {
            return (Func<TType>)factory;
        }

        return CreateCachedFactory<TType>(typeofType);
    }

    private static Func<TType> CreateCachedFactory<TType>(Type typeofType) {
        var ctorInfo = typeofType.GetConstructor(Type.EmptyTypes);
        var lambdaExpression = Expression.Lambda<Func<TType>>(Expression.New(ctorInfo));
        var factory = lambdaExpression.Compile();

        dictionary.Add(typeofType, factory);

        return factory;
    }
}

public class MyClass
{
}

static class Program
{

    static void Main() {
        var myClassFactory = TypeFactory.CreateFactory<MyClass>();
        var instance = myClassFactory();

        Console.WriteLine(instance.GetType().FullName);
    }
}

Behind the scenes Expression trees is using lcg or light code generation which constructs the method using Reflection.Emit which is much harder to comprehend, so you might say that expression trees are also an abstraction for code generation.

Sagi
  • 8,972
  • 3
  • 33
  • 41
2

It is used often when you need to parse this expression, for example determine the expression parameters names or something like that. For example, there is ArgumentNullException in .NET, to create an instance of this, you shoud pass the parameter name as string, it is not type-safe approach, so you can use this wrapper:

    public static string GetName<T>(this Expression<Func<T>> expr)
    {
        if (expr.Body.NodeType == ExpressionType.MemberAccess)
            return ((MemberExpression)expr.Body).Member.Name;

        if (expr.Body.NodeType == ExpressionType.Convert
            && ((UnaryExpression)expr.Body).Operand.NodeType
            == ExpressionType.MemberAccess)
            return ((MemberExpression)((UnaryExpression)expr.Body).Operand)
                .Member.Name;

        throw new ArgumentException(
            "Argument 'expr' must be of the form ()=>variableName.");
    }

    public static void CheckForNullArg<T>(params Expression<Func<T>>[] expressions)
    {
        foreach (var expression in expressions)
        {
            if (EqualityComparer<T>.Default.Equals(expression.Compile()(), default(T)))
            {
                throw new ArgumentNullException(expression.GetName());
            }
        }
    }

    public void Test(string parameter)
    {
        CheckForNullArg(() => parameter);
    }

Another similar use is type-safe handling PropertyChanged event in WPF - Handling PropertyChanged in a type-safe way

Besides that, it is useful for logging, because you can save the string representation of Expression in a log, calling ToString().

Community
  • 1
  • 1
olegk
  • 765
  • 4
  • 13
  • In my opinion this is a dirty hack that can now die in piece since the nameof operator in c# 6 and the CallerMemberName Attribute of c# 5 – quadroid Sep 28 '15 at 08:08
  • 1
    Side note your code throws a argument null exception if T is an Integer and its value is 0, better use a generic type constraint. – quadroid Sep 28 '15 at 08:11
  • 2
    Absolutely agree, it is just an example. But, at the same time, not everyone can afford to use new versions of C#, because, e.g. unwilingness to enforce user to install new .NET versions. – olegk Sep 28 '15 at 08:15
  • Honestly, sometimes C# feels overengineered to oblivion, in part I think because of heavily promoting OOP. – Paul-Sebastian Manole Nov 01 '22 at 17:44