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 >>
4
  • Have you tried dynamic? Commented Oct 19, 2018 at 13:28
  • @FrankerZ You mean change the statement like Expression.Lambda<Func<dynamic, dynamic, dynamic>>(call, instance, input).Compile(); ? It doesn't work, same error. Commented Oct 19, 2018 at 13:35
  • Possible duplicate of How do I use reflection to call a generic method? Commented Oct 19, 2018 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. Commented Oct 19, 2018 at 13:41

1 Answer 1

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);
Sign up to request clarification or add additional context in comments.

4 Comments

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 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
Thanks, just to clarify what I mean. <Program, bool, int>, 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 })
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

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.