Using Func<> as an intermediary

How Func or Action can be used as an intermediary when calling methods

Home DailyDrop

Daily Knowledge Drop

A Func (or Action) can be used as an intermediary to keep code cleaner when dealing with multiple methods or delegates of the same signature.


The setup

The root of this post stems from a real-world situation I'd encountered. The examples below will be a simplified example of the situation but the setup is as follows:

  • A handler is obtained from the dependency injection container
  • A collection of none or many interceptors are obtained from the dependency injection container
  • If no interceptors are obtained, then Invoke a HandleOperation method on the handler
  • If any interceptors are obtained, then build up a "pipeline" of all interceptors and then the handler at the end of the pipeline.
  • Each interceptor would perform any logic it might need to, and proceed to the next interceptor or handler, if at the end of the pipeline (similar to the ASPNET Core middleware pipeline functions)

The Code

Non-working example

Below is a simplified version of the above situation.

If there is an Interceptor supplied, then its InterceptValue method is called, otherwise the Handler HandleOperation method is called:

This will NOT compile:

void PerformOperation(int x, IInterceptor interceptor = null)
{
    var handler = new Handler();

    var start = interceptor != null ? interceptor.InterceptValue : handler.HandleOperation;

    Console.WriteLine($"Log: About to perform the operation");

    var result = start.Invoke(x);

    Console.WriteLine($"Log: The result of the operation is: {x}");
}

The error originating on line 5 of the above code is: Type of conditional expression cannot be determined because there is no implicit conversion between 'method group' and 'method group'

Even though both methods, InterceptValue and HandleOperation have the same signature, they are considered completely different types.
As per the error message, the compiled can't determine the type of var start because the two methods are different types and either could be assigned to var start.

To fix this, the compiler need to be explicitly how to convert the two different methods to one common underlying type.


Working example

To tell the compiler how to convert the two different methods to the same common type, the var just needs to be replaced with the common type.

The commonality between InterceptValue and HandleOperation is that they are both methods, which accept one int parameter, and return an int - luckily C# has a way to represent a method as a variable, usingFunc<int, int> (in this example).

For reference - just like an int represents a numerical value, a Func represents a method value. The generic parameters <int, int> refer to the parameter type and return type of the method.

void PerformOperation(int x, IInterceptor interceptor = null)
{
    var handler = new Handler();

    Func<int, int> start = interceptor != null ? 
        interceptor.InterceptValue : handler.HandleOperation;

    Console.WriteLine($"Log: About to perform the operation");

    var result = start.Invoke(x);

    Console.WriteLine($"Log: The result of the operation is: {x}");
}

Above declares a variable of type "Method", a method which takes one parameter of type int and returns an int - and the value of this variable is either InterceptValue or HandleOperation. This Func (method) is then invoked, with the parameter value being passed in.

This will now compile and work!


Notes

Using Func or Action allow for the reduction of methods, with the same signatures, to a common type - this is useful in the situation where one might have to run through a long list of logic to determine which method to invoke. The method can be treated as a variable and assigned and reassigned as the logic is executed.


Daily Drop 36: 23-03-2022

At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.

The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.
c# .net func methods invoke