2

My programme will get service name and method name during its run time, and to execute the method dynamically I m creating a lambda expression based func.

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;
}

but it won't and it will throw error at run time

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

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

System.ArgumentException: 'ParameterExpression of type 'UnitTests.ITestDemoService' cannot be used for delegate parameter of type 'System.Object''

Is there a way to specify the type for the Expression.Lambda?

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

to

Expression.Lambda<Func<inputServiceType ,inputType , outputType >>
Liam
  • 27,717
  • 28
  • 128
  • 190
ValidfroM
  • 2,626
  • 3
  • 30
  • 41
  • Have you tried [`dynamic`](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/using-type-dynamic)? – Blue Oct 19 '18 at 13:28
  • @FrankerZ You mean change the statement like Expression.Lambda>(call, instance, input).Compile(); ? It doesn't work, same error. – ValidfroM Oct 19 '18 at 13:35
  • Possible duplicate of [How do I use reflection to call a generic method?](https://stackoverflow.com/questions/232535/how-do-i-use-reflection-to-call-a-generic-method) – thehennyy Oct 19 '18 at 13:37
  • 1
    You might try something like: `var returnType = typeof(Func<>).MakeGenericType(new[] { inputServiceType, inputType, outputType });` There is an overload which uses the first parameter as return type instead of passing it as generic method. – Jeroen van Langen Oct 19 '18 at 13:41

1 Answers1

1

Your expression lacks a type casts. To make it compile you need explicitly convert object to inputServiceType and so on. Try this code:

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;

Try it out here


EDIT You can make it more type safe:
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();
}

Usage:

var func = CreateTypedLambdaExpression<Program, bool, int>("TestMethod");
var result = func(service, false);
Aleks Andreev
  • 7,016
  • 8
  • 29
  • 37
  • That's nice,it works. Thanks. Do you know if there is a way to specify these object type to its run time type? I know it is working already. Maybe we are not too far away from specifing proper types of this lambdaFunc, thus it will be a real strong type func . – ValidfroM Oct 19 '18 at 15:25
  • @ValidfroM please check my edit. Now this code can create strongly typed lambdas. However now you are responsible for type checking and providing correct type arguments – Aleks Andreev Oct 19 '18 at 15:44
  • Thanks, just to clarify what I mean. , I can't specify these types in the code, they are all based on the configuration read in runtime. So I'm tring sth like typeof(Func<>).MakeGenericType(new[] { inputServiceType, inputType, outputType }) – ValidfroM Oct 19 '18 at 15:49
  • If you do not know exact types at compile time your only option is to use `object`. However first version of my answer already have runtime type check. If `Convert` fails it will throw an exception – Aleks Andreev Oct 19 '18 at 16:05