Как создать лямбда-выражение с динамическими типами - PullRequest
0 голосов
/ 19 октября 2018

Моя программа получит имя службы и имя метода во время своего выполнения, и для динамического выполнения метода я создаю функцию, основанную на лямбда-выражениях.

public static Func<object,object,object> CreateLambdaExpression2(string tService, string methodName)
{
    var inputServiceType = Type.GetType(tService);
    var methodInfo = inputServiceType.GetMethod(methodName);
    var inputType = methodInfo.GetParameters().First().ParameterType;
    var outputType = methodInfo.ReturnParameter.ParameterType;

    var instance = Expression.Parameter(inputServiceType, "serviceInstance");
    var input = Expression.Parameter(inputType, "inputData");
    var call = Expression.Call(instance, methodInfo, input);

    var lambdaFunc = Expression.Lambda<Func<object,object, object>>(call, instance, input).Compile(); //<= this line throws the error.
    return lambdaFunc;
}

, но не будет и будет выдаватьошибка во время выполнения

var compiledMethod = ServiceMapper.CreateLambdaExpression2(tService,"Get");

var serviceInstance = new TestDemoService();
var inputData = new TestDemoPersonRequest()
{
    Id = 555
};
var result = compiledMethod(serviceInstance, inputData);

System.ArgumentException: 'ParameterExpression типа' UnitTests.ITestDemoService 'не может использоваться для параметра делегата типа' System.Object ''

Есть ли способ указать тип для Expression.Lambda?

Expression.Lambda<Func<object,object, object>>

до

Expression.Lambda<Func<inputServiceType ,inputType , outputType >>

1 Ответ

0 голосов
/ 19 октября 2018

В вашем выражении отсутствует тип приведений.Для его компиляции вам нужно явно преобразовать object в inputServiceType и так далее.Попробуйте этот код:

var objType = typeof(object);
var instance = Expression.Parameter(objType, "serviceInstance");
var input = Expression.Parameter(objType, "inputData");
var call = Expression.Call(
    Expression.Convert(instance, inputServiceType), // convert first arg 
    methodInfo,
    Expression.Convert(input, inputType)); // and second
var body = Expression.Convert(call, objType); // and even return type

var lambdaFunc = Expression.Lambda<Func<object, object, object>>(body, instance, input).Compile();
return lambdaFunc;

Попробуйте здесь

РЕДАКТИРОВАТЬ Вы можете сделать его более безопасным для типов:

public static Func<TService, TInput, TReturn>
    CreateTypedLambdaExpression<TService, TInput, TReturn>(
        string methodName)
{
    var inputServiceType = typeof(TService);
    var methodInfo = inputServiceType.GetMethod(methodName);
    var inputType = typeof(TInput);

    // now you need to check if TInput is equal to methodInfo.GetParameters().First().ParameterType
    // same check for return type

    var instance = Expression.Parameter(inputServiceType, "serviceInstance");
    var input = Expression.Parameter(inputType, "inputData");
    var call = Expression.Call(instance, methodInfo, input);

    var lambdaFunc = Expression.Lambda<Func<TService, TInput, TReturn>>(call, instance, input);
    return lambdaFunc.Compile();
}

Использование:

var func = CreateTypedLambdaExpression<Program, bool, int>("TestMethod");
var result = func(service, false);
...