2

I'm not able to find the way how to compile the deserialized Expression using Serialize.Linq. The accepted answer in "Compile Expression at runtime Using Serialize.Linq" doesn't work (part 2 below):

using System.Linq.Expressions;
using Serialize.Linq.Extensions;
using Serialize.Linq.Serializers;

namespace ConsoleApp3;

internal class Program {

    static void Main() {
        Expression<Func<double, double, double>> distanceCalc =
            (x, y) => Math.Sqrt(x * x + y * y);
        var xParameter = Expression.Parameter(typeof(double), "x");
        var yParameter = Expression.Parameter(typeof(double), "y");
        var xSquared = Expression.Multiply(xParameter, xParameter);
        var ySquared = Expression.Multiply(yParameter, yParameter);
        var sum = Expression.Add(xSquared, ySquared);
        var sqrtMethod = typeof(Math).GetMethod("Sqrt", new[] { typeof(double) }) ?? throw new InvalidOperationException("Math.Sqrt not found!");
        var distance = Expression.Call(sqrtMethod, sum);
        var distanceLambda = Expression.Lambda<Func<double, double, double>>(
            distance,
            xParameter,
            yParameter);

        var serializer = new ExpressionSerializer(new Serialize.Linq.Serializers.JsonSerializer());
        string serializedExpression = serializer.SerializeText(distanceLambda);

        // part 1 works
        var de1 = serializer.DeserializeText(serializedExpression).ToExpressionNode().ToExpression<Func<double, double, double>>();
        var espre1 = de1.Compile();
        Console.WriteLine(espre1(3, 4));

        // part 2 as per "Compile Expression at runtime Using Serialize.Linq" doesn't work
        var de2 = serializer.DeserializeText(serializedExpression) as LambdaExpression;
        var espre2 = de2.Compile();
        // Console.WriteLine(espre2(3, 4)); compiler error on espre2 "Method name expected"

        // part 3 another problem
        var de3 = serializer.DeserializeText(serializedExpression).ToExpressionNode().ToExpression<Func<double, double, double>>();
        var t3 = de3.GetType();
        var tm3 = t3.GetMethod("Compile", []);
        var espre3 = tm3.Invoke(de3, []); // expre3 shows "Internal error evaluating expression" in debug
        // Console.WriteLine(espre3(3, 4)); compiler error on espre3 "Method name expected"
    }
}

Of cause, part 1 is not the way because "ToExpression<Func<double, double, double>>()" can't be applied (unknown).

Also, please explain the problem in the part 3

4
  • 1
    espre2.DynamicInvoke(3, 4) - second will work, have no clue what you trying in third one Commented Apr 7, 2024 at 4:11
  • 1
    @PowerMouse Thank you, it works! In the third part, I get the method Compile over reflection and apply it to the object. I expect this to work as well as in part 1.
    – rotabor
    Commented Apr 7, 2024 at 7:10
  • Are you trying to do this for serialized expressions you don't know the signature of?
    – Corey
    Commented Apr 7, 2024 at 9:00
  • @Corey It looks like so. Generally, it's not available in the receiver of the serialized expression in the compilation time.
    – rotabor
    Commented Apr 7, 2024 at 10:19

1 Answer 1

1

Given that you are trying to handle the general case where you aren't necessarily going to know the method signature, that leaves you with plain LambdaExpression compiling to Delegate.

Let's assume that you have the following:

  • The function serialized as JSON.
  • An object array of parameters to pass into the function.

The process is to deserialize the function to a LambdaExpression, compile it to Delegate and then call Invoke. Unfortunately Delegate doesn't allow direct invocation, but you can use the Method property to access the compiled method, then invoke that.

Something similar to this:

static object? ExecuteSerializedFunction(string serializedFunction, object?[] parameters)
{
    ExpressionSerializer serializer = new(new JsonSerializer());
    LambdaExpression? expression = serializer.DeserializeText(serializedFunction)
        .ToExpressionNode()
        as LambdaExpression;
    
    // Compile to a Delegate with any signature
    Delegate? function = expression?.Compile();
    
    // Get the method to execute
    MethodInfo? method = funcion?.Method;
    
    // In my testing compilation adds a 'Capture' parameter - adjust as required.
    object?[] actualParameters = [null, ..parameters]

    // Execute the method and return the result.
    // NB: There is no instance variable for the invocation.
    return method?.Invoke(null, actualParameters);
}

This is quite simplified, and there's a good chance there will be unexpected things in the required parameter list. I added an empty 'Capture' parameter in the code above, but you'll want to do a lot more than that. If you get the wrong parameter list, Invoke will throw an exception.

(Also, handle exceptions. This is just a minimal example.)

2
  • ".ToExpressionNode()" is extra and causes an error. Also ".Method.Invoke(null, [null, 3, 4 ])" looks more complex than ".DynamicInvoke(3, 4)"
    – rotabor
    Commented Apr 8, 2024 at 6:46
  • @rotabor DynamicInvoke may work for you, it may not. Try it and see. The parameters get boxed to object?[]? either way. And I copied part of your deserialize code because I don't use the library.
    – Corey
    Commented Apr 8, 2024 at 8:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.