Morgan
Morgan

Reputation: 486

C# Expression, transform and combine an expression

I have a method like this :

public static Expression<Func<T, IEnumerable<byte>>> GetSerializer(Expression<Func<T, int>> expr)
{
    return m => BitConverter.GetBytes();
}

T is a class with different member type : int, string, bool or custom classes. I would like to know if it is possible in this method to make an expression where I serialize the int variable from parameters to byte[] and return it. My goal is to execute this code later.

It is only theorical, I don't want to serialize an int, just trying to understand how Expressions work.

Upvotes: 1

Views: 173

Answers (1)

Krzysztof
Krzysztof

Reputation: 16140

To make expression that invokes other expression, you should build it dynamically. Example:

    public static Expression<Func<T, IEnumerable<byte>>> GetSerializer<T>(Expression<Func<T, int>> expr)
    {
        // Find GetBytes method which accepts int as an argument
        var method = typeof(BitConverter).GetMethod("GetBytes", BindingFlags.Static | BindingFlags.Public, null,
            CallingConventions.Any, new[] {typeof(int)}, null);

        // Input parameter for lambda -> (T p)
        var p = Expression.Parameter(typeof(T));

        // Body of lambda -> BitConverter.GetBytes(expr(p))
        var body = Expression.Call(method, Expression.Invoke(expr, p));

        // Build lambda -> (T p) => BitConverter.GetBytes(expr(p))
        return Expression.Lambda<Func<T, IEnumerable<byte>>>(body, p);
    }

Note that it looks up for GetBytes method during runtime, so even if method BitConverter.GetBytes(int) doesn't exist it will compile.

You can go even further and make this method fully generic:

    public static Expression<Func<T, IEnumerable<byte>>> GetSerializer<T, T2>(Expression<Func<T, T2>> expr)
        where T2 : struct
    {
        // Find GetBytes method which accepts T2 as an argument
        var method = typeof(BitConverter).GetMethod("GetBytes", BindingFlags.Static | BindingFlags.Public, null,
            CallingConventions.Any, new[] {typeof(T2)}, null);

        // Check if method exists
        if (method == null)
        {
            throw new ArgumentException("Invalid type provided");
        }

        // Input parameter for lambda -> (T p)
        var p = Expression.Parameter(typeof(T));

        // Body of lambda -> BitConverter.GetBytes(expr(p))
        var body = Expression.Call(method, Expression.Invoke(expr, p));

        // Build lambda -> (T p) => BitConverter.GetBytes(expr(p))
        return Expression.Lambda<Func<T, IEnumerable<byte>>>(body, p);
    }

Upvotes: 1

Related Questions