It is always nice to learn something new, regardless of our experience. In this short but very cool post, we’ll learn more about a little known feature having to do with Action, Func, and Predicate in .NET.
A delegate is a type that represents a reference to a method with a particular set of parameters and return type. Delegates are one of the fundamental building blocks of the .NET framework, and they received a significant upgrade with the introduction of the Language Integrated Query (LINQ) syntax. You may also here delegates referred to as Lambda Expressions.
There are three recognized Lambda expressions: Actions, Funcs, and Predicates.
An Action is an expression that takes no parameters but executes a statement.
A Func is an expression that can take any number of parameters, including no parameters, and must return a result.
A Predicate is a specific kind of construct similar to Func that takes in one of parameter and returns a bool result.
As mentioned before, all of these lambda expressions inherit their behavior from the delegate type.
Chaining Behaviors
Delegates allow developers to add as many handlers to a delegate instance utilizing the += operator. Let’s walk through the invocation behavior of each type.
Action Chain Behavior
With an Action, we can chain any number of actions after the first assignment.
Executing this code yields the following results.
As we can see, the app executes the actions based on their registration.
Func Chain Behavior
A Func behaves slightly differently, as it could return a result. Let’s look at an example.
What should we expect the result to be? Well, we don’t have to guess.
Executing the chained Func will always return the last result. We could get the individual results by using the GetInvocationList method and perform each Func independently.
We now get each result for the individual Func instances.
Predicate Chain Behavior
Like a Func, Predicate behave similarly, but need to be defined explicitly, meaning we can’t use var here.
The result of invoking isItCake yields a positive outcome.
As we may have noticed, the result is that of the last registered Predicate. We can use the GetInvocationList method to iterate over each predicate instance.
Each delegate yields a different result.
Conclusion
Chaining is a powerful feature of .NET delegates and many developers may not realize that when passed an Action, Func, or Predicate they may be getting more than one. When writing libraries that pass these types around, it may be necessary for library authors to check the GetInvocationList and react accordingly: throw an exception, invoke each instance and aggregate the results, or do nothing different.
I hope you learned something new and exciting, and please leave a comment if you have any thoughts on the subject.
Photo by 