Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Box and Unbox in ExpressionParameter or how to generically wrap every method

I have some domain which execute business logic. And I want to wrap every domain method with another method, which can extend the business logic. I do this with expressions. Everything works fine as long as the domain methods have parameters of reference types. As soon as they have a value typed parameter, my call to Expression.NewArrayInit crashes, because int cannot be used to initialize an array of type object. I know, that the solution is boxing and unboxing, but I don’t know how.

This is my example code. I have two NUnit tests. The first one is for the domain method with reference type parameters, the second one is for the domain method with one value type parameter. I struggle with the second one. (By the way: I simplified the domain methods and the wrapping method.)

    [Test]
    public void Should_work_with_reference_types()
    {
        var domainBla = "This is the original Bla-Parameter";
        object domainBlubber = "This is the original Blubber-Parameter";

        var wrappedDomainMethod = BuildWrappedDomainMethod(new Action<string, object>(DomainMethod), "Before calling DomainMethod");

        wrappedDomainMethod.DynamicInvoke(domainBla, domainBlubber);
    }

    [Test]
    public void Should_work_with_value_types()
    {
        var domainBla = "This is the original Bla-Parameter";
        int domainBlubber = 73;

        var wrappedDomainMethod = BuildWrappedDomainMethod(new Action<string, int>(AnotherDomainMethod), "Before calling AnotherDomainMethod");

        wrappedDomainMethod.DynamicInvoke(domainBla, domainBlubber);
    }

    public Delegate BuildWrappedDomainMethod(Delegate domainMethod, string message)
    {
        var thisInstanceExpression = Expression.Constant(this);
        var wrappingMethodInfo = GetMethodInfo<ExpressionUnderstandingFixture>(f => f.WrappingMethod(null, null, null));
        var messageExpression = Expression.Constant(message);
        var domainMethodExpression = Expression.Constant(domainMethod);
        var parametersList = domainMethod.Method.GetParameters().Select(pi => Expression.Parameter(pi.ParameterType)).ToList();
        var parametersArrayNewExpression = Expression.NewArrayInit(typeof(object), parametersList);
        var wrappedMethodCallExpression = Expression.Call(
            thisInstanceExpression,
            wrappingMethodInfo,
            messageExpression,
            domainMethodExpression,
            parametersArrayNewExpression);
        var wrappedMethodCallLambdaExpression = Expression.Lambda(
            domainMethod.GetType(),
            wrappedMethodCallExpression,
            parametersList);
        var wrapedCallLambda = wrappedMethodCallLambdaExpression.Compile();
        return wrapedCallLambda;
    }

    public void WrappingMethod(string message, Delegate domainMethod, object[] parameters)
    {
        Console.WriteLine($"Message: {message}");
        domainMethod.DynamicInvoke(parameters);
    }

    public void DomainMethod(string bla, object blubber)
    {
        Console.WriteLine($"bla: {bla}");
        Console.WriteLine($"blubber: {blubber}");
    }

    public void AnotherDomainMethod(string bla, int blubber)
    {
        Console.WriteLine($"bla: {bla}");
        Console.WriteLine($"blubber: {blubber}");
    }

    public MethodInfo GetMethodInfo<T>(Expression<Action<T>> expression)
    {
        var member = expression.Body as MethodCallExpression;

        if (member != null)
        {
            return member.Method;
        }

        throw new ArgumentException("Expression is not a method", "expression");
    }

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

You can use Expression.Convert to box value type into object, and you can leave reference types as is:

var parametersArrayNewExpression = Expression.NewArrayInit(typeof(object), 
    parametersList.Select(x => x.Type.IsValueType ? (Expression) Expression.Convert(x, typeof(object)) : x));
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading