Rufen Sie eine MethodCallExpression mit einer dynamischen Anzahl von Parametern auf

c# expression-trees

Frage

Ich muss folgendes nennen:

public MethodCallExpression CreateLazyMethod(object instance, MethodBase foundMethodInfo)
{
    var orderedParameters = MethodParametersEvaluator.OrderParameters(foundMethodInfo.GetParameters());
    var paramExpressions = orderedParameters.Select(x => (Expression)Expression.Parameter(x.GetType())).ToArray();
    return Expression.Call((MethodInfo)foundMethodInfo, paramExpressions);
}

und die Parameter zur Laufzeit auflösen

var callabel = CreateLazyMethod(instance, foundMethodInfo);
var runtimeEvaluatedParamsList=new object[]{1,654,};
callable.Invoke(runtimeEvaluatedParamsList);

Irgendwelche Lösungen?

Akzeptierte Antwort

Sie können es als object[] senden und dann während der Methodengenerierung ein bestimmtes Element verwenden.

Grundsätzlich für "Test Long String".Substring(5); Der folgende Code erzeugt eine solche Methode:

object DynamicMethod(object[] params)
{
    return "Test Long String".Substring((int)params[0]);
}

Nachteil ist, dass Ergebnis und Parameter Objekt sind, so dass es nicht benötigte Boxing- und Unboxing-Operationen erzeugt, aber wenn Sie die Signatur kennen, können Sie eine generische Implementierung schreiben, die bestimmte Typen verwendet.

Beispielcode:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

public class Program
{
    public static void Main(string[] args)
    {
        // test non static method with result
        string test = "Test String";
        MethodInfo method = typeof(string).GetMethod("Substring", BindingFlags.Public | BindingFlags.Instance, null, new Type[]{typeof(int)},null);
        Func<object[], object> lazyMethod = CreateLazyMethodWithResult(test, method);
        object result = lazyMethod(new object[] { 5 });
        Console.WriteLine(result);
        Console.WriteLine();

        // test static method with no result
        var staticMethod = typeof(Program).GetMethod("StaticMethod", BindingFlags.Static | BindingFlags.Public);
        var staticAction = CreateLazyStaticMethodWithNoResult(staticMethod);

        staticAction(new object[]{"Test message"});
        Console.WriteLine();

        //test static method with result
        var staticMethodWithResult = typeof(Program).GetMethod("StaticMethodWithResult", BindingFlags.Static | BindingFlags.Public);
        var staticActionWithResult = CreateLazyStaticMethodWithResult(staticMethodWithResult);

        Console.WriteLine(staticActionWithResult(new object[] { "Test message" }));
        Console.WriteLine();

        // sample with constructor
        var constructorCall = typeof(TestClass).GetConstructors().First();
        var constructorAction = GenerateLazyConstructorCall(constructorCall);

        var createdObject = constructorAction(new object[] { "Test message" });

        Console.WriteLine("Created type is " + createdObject.GetType().FullName);
    }

    //Test class
    public class TestClass
    {
        public TestClass(string message)
        {
            Console.WriteLine("----Constructor is called with message - " + message);
        }
    }

    public static void StaticMethod(string message)
    {
        Console.WriteLine("----Static method is called with " + message);
    }

    public static string StaticMethodWithResult(string message)
    {
        Console.WriteLine("----Static method with result is called with " + message);
        return "Hello from static method";
    }

    public static Func<object[], object> CreateLazyMethodWithResult(object instance, MethodInfo method)
    {
        ParameterExpression allParameters;
        var methodCall = GenerateCallExpression(instance, method, out allParameters);
        var lambda = Expression.Lambda<Func<object[], object>>(methodCall, allParameters);
        return lambda.Compile();
    }

    public static Action<object[]> CreateLazyMethodWithNoResult(object instance, MethodInfo method)
    {
        ParameterExpression allParameters;
        var methodCall = GenerateCallExpression(instance, method, out allParameters);
        var lambda = Expression.Lambda<Action<object[]>>(methodCall, allParameters);
        return lambda.Compile();
    }

    public static Func<object[], object> CreateLazyStaticMethodWithResult(MethodInfo method)
    {
        ParameterExpression allParameters;
        var methodCall = GenerateCallExpression(null, method, out allParameters);
        var lambda = Expression.Lambda<Func<object[], object>>(methodCall, allParameters);
        return lambda.Compile();
    }

    public static Action<object[]> CreateLazyStaticMethodWithNoResult(MethodInfo method)
    {
        ParameterExpression allParameters;
        var methodCall = GenerateCallExpression(null, method, out allParameters);
        var lambda = Expression.Lambda<Action<object[]>>(methodCall, allParameters);
        return lambda.Compile();
    }


    /// <summary>
    /// Generate expression call
    /// </summary>
    /// <param name="instance">If instance is NULL, then it method will be treated as static method</param>
    private static MethodCallExpression GenerateCallExpression(object instance, MethodBase method, out ParameterExpression allParameters)
    {
        var parameters = GenerateParameters(method, out allParameters);

        var methodInfo = method as MethodInfo;
        // it's non static method
        if (instance != null)
        {
            var instanceExpr = Expression.Convert(Expression.Constant(instance), instance.GetType());
            return Expression.Call(instanceExpr, methodInfo, parameters.ToArray());
        }

        // it's static method
        return Expression.Call(methodInfo, parameters.ToArray());
    }

    public static Func<object[], object> GenerateLazyConstructorCall(ConstructorInfo constructor)
    {
        ParameterExpression allParameters;
        var parameters = GenerateParameters(constructor, out allParameters);

        var newExpr = Expression.New(constructor, parameters.ToArray());
        var lambda = Expression.Lambda<Func<object[], object>>(newExpr, allParameters);

        return lambda.Compile();
    }

    private static List<Expression> GenerateParameters(MethodBase method, out ParameterExpression allParameters)
    {
        allParameters = Expression.Parameter(typeof(object[]), "params");
        ParameterInfo[] methodMarameters = method.GetParameters();
        List<Expression> parameters = new List<Expression>();
        for (int i = 0; i < methodMarameters.Length; i++)
        {
            var indexExpr = Expression.Constant(i);
            var item = Expression.ArrayIndex(allParameters, indexExpr);
            var converted = Expression.Convert(item, methodMarameters[i].ParameterType);
            parameters.Add(converted);
        }

        return parameters;
    }
}

Wirkliches Arbeitsbeispiel: https://dotnetfiddle.net/lHB2kE

UPDATE I aktualisierte Code mit Beispielen für Aufrufmethoden und Konstruktor:

  1. Instanzmethode ohne Rückgabeobjekt
  2. Instanzmethode mit einem Rückgabeobjekt
  3. Statische Methode ohne Rückgabeobjekt
  4. Statische Methode mit einem Rückgabeobjekt
  5. Verwendung des Beispielkonstruktors


Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum