.NET Things: Delegates

James Louie
Pragmatic Programming
4 min readMar 7, 2019

--

Continuing on with the .NET Things series, we’re going to explore C# delegates — put simply they are functions that are treated as first class objects, and therefore can be passed around as parameters in your code. We’ll see how C# exposes this API, some common places ASP.NET uses them, and go over some common use cases.

Quick Peek

Here’s an example of what a delegate looks like:

The first thing you will notice is the parameter func has a unique type. This signature Func<int, int, int> isn’t as complex as it looks. The last parameter int represents the return type of the function, and the preceding types specify the inputs of the function. Putting this together, this function takes two int types and returns an int.

The next thing to recognize is how to call a method with a delegate. The syntax (a,b) => a * b indicates that your function takes two parameters, and the result of your statement is an int, which matches the parameter signature.

Functions and Actions

Delegates come in two flavors: Functions and Actions.

Function: Func<...Input,Output>

Function are delegates that take in an output parameter and a series of input parameters and acts as a standard function.

Action: Action<...Input>

Actions are delegates that only take inputs and does not return a result. This can be treated as a void return statement.

Named Delegates

C# allows you to create named delegates to help you reason about the functions a bit better.

In this example, we define a named delegate Del which takes in an string x and returns void.

We can assign the function DoWork to Del d because they share the same method signature. When we call d(…), it will pass in our string into the DoWork function and write to the console. We can reuse this mechanism to describe more complex cases with a name, rather than be forced to reason what the code is doing by reading the it.

Examples, Examples Everywhere!

Mvc Configuration

In a previous post for adding request validation, we glossed over how to configure the Mvc pipeline to use our validator filter. Now we can come back and to describe how this piece works.

The method signature of AddMvc looks like this: public static IMvcBuilder AddMvc (this IServiceCollection services, Action<MvcOptions> setupAction). Using this pattern, ASP.NET exposes the MvcOptions object, which we can then manipulate the configuration object which will then be processed by Mvc.

LINQ

Now that we have everything clicking, I thought I’d blow your mind by saying that LINQ is almost largely written with delegates in mind.

If you hover over Where, you’ll see that the signature is Func<T, bool>, where T is the caller class and the result is a boolean to determine whether to filter the entity and.

The source code for Where function can be found on Github repository.

Common Use Cases

Delegates are a very simply technique that can be applied in a variety of ways. I’ll go over a few common uses cases that get your mind going on the possibilities and you will probably be able to find many more uses.

Configuration

As we saw in Mvc configuration, Actions are a great way to expose an object to the end user for them to manipulate before further processing. By instantiating the object within the function, you are able to have more control over the life cycle of this object, but have the flexibility of letting the user configure it.

Callback (Event Driven)

Very common in event driven systems, you can use callbacks to represent the behavior after some condition has been triggered. For example on button click, you may want to activate some function. This is a very popular approach to working asynchronously when your language does not have good async/await patterns to represent asynchronous behavior.

Function Wrappers

When you want to do some actions before and/or after a function. This can be some cache strategy, performance evaluation, logging, response translation, and any combination of these and many more. This approach is very powerful when used with generic type parameters, as it increases the scope of functions that it can apply to. This is a great way to examine your code to reduce code repetition.

Iteration over Collection

This is already done mostly for you through the LINQ methods, but this is a great use case where you want to apply your function to a collection of objects. The Where clause is great for filtering, Select is good for projecting a collection to a different collection, and Single and First are great for finding some element. All of these functions are built on top of the idea of delegates and applying some function over a collection.

Empower Your Code with Delegates!

Delegates are a great way to identify common flows or processes, but want to maintain the flexibility of usage. This design has helped me follow the D.R.Y principle by exposing a mechanism for representing functions that are extendable with other functions. I’d love to hear how you’ve used them and helped you write clean code!

--

--

James Louie
Pragmatic Programming

Developer looking to make the code a little more clean