Earn More by Sharing What You Love
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Tim Corey
1h 9m 11s
Delegates in C# are a powerful feature, yet many developers are not familiar with their effective use. Tim Corey's video on "Delegates in C# - A practical demonstration, including Action and Func" provides a thorough explanation of what delegates are, how to use them, and why they are useful.
This article will provide you with Tim's expert insights on delegates in C#, offering a clear explanation of their usage and practical applications. You'll learn how delegates can enhance the flexibility and efficiency of your code, with examples like their use in a shopping cart system.
Tim introduces the concept of delegates, highlighting their power and versatility in C#. He assures viewers that despite some intimidating terminology, the foundation of delegates is simple. Tim aims to demystify delegates and cover special types like func and action.
Tim sets up a demo application to illustrate the use of delegates. The solution contains three projects: a Console UI, a Demo Library, and a WinForm UI. The focus initially is on the Console UI and Demo Library.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleUI
{
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal():C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
}
}
public class ShoppingCartModel
{
public List<ProductModel> Items { get; set; } = new List<ProductModel>();
public decimal GenerateTotal()
{
decimal subtotal = Items.Sum(x => x.Price);
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
}
public class ProductModel
{
public string ItemName { get; set; }
public decimal Price { get; set; }
}Imports System
Imports System.Collections.Generic
Imports System.Linq
Namespace ConsoleUI
Class Program
Private Shared cart As New ShoppingCartModel()
Shared Sub Main(args As String())
PopulateCartWithDemoData()
Console.WriteLine($"The total for the cart is {cart.GenerateTotal():C2}")
Console.ReadLine()
End Sub
Private Shared Sub PopulateCartWithDemoData()
cart.Items.Add(New ProductModel With {.ItemName = "Cereal", .Price = 3.63D})
cart.Items.Add(New ProductModel With {.ItemName = "Milk", .Price = 2.95D})
cart.Items.Add(New ProductModel With {.ItemName = "Strawberries", .Price = 7.51D})
cart.Items.Add(New ProductModel With {.ItemName = "Blueberries", .Price = 6.75D})
End Sub
End Class
End Namespace
Public Class ShoppingCartModel
Public Property Items As List(Of ProductModel) = New List(Of ProductModel)()
Public Function GenerateTotal() As Decimal
Dim subtotal As Decimal = Items.Sum(Function(x) x.Price)
If subtotal > 100 Then
Return subtotal * 0.80D
ElseIf subtotal > 50 Then
Return subtotal * 0.85D
ElseIf subtotal > 10 Then
Return subtotal * 0.90D
Else
Return subtotal
End If
End Function
End Class
Public Class ProductModel
Public Property ItemName As String
Public Property Price As Decimal
End ClassTim explains the structure and functionality of the demo application:
Tim walks through the discount logic in the GenerateTotal method, explaining how the subtotal determines the discount applied:
Tim uses a breakpoint to demonstrate the calculation and discount logic, ensuring viewers understand the foundation before introducing delegates.
In this section, Tim Corey dives into the concept of delegates in C#, explaining how they work and demonstrating their use with practical code examples.
Tim explains that a delegate is essentially a way to pass methods as parameters. Instead of passing a variable or a property, you pass a method, which allows for more flexible and reusable code.
Here's how Tim breaks down the process of creating and using a delegate:
Define the Delegate:
The delegate is defined at the top of the class, specifying the return type and parameter types.
public delegate void MentionDiscount(decimal subtotal);Public Delegate Sub MentionDiscount(subtotal As Decimal)This delegate specifies a method that returns void and takes a decimal as a parameter.
Using the Delegate in a Method:
The delegate is used as a parameter in the GenerateTotal method of the ShoppingCartModel class.
public decimal GenerateTotal(MentionDiscount mentionDiscount)
{
decimal subtotal = Items.Sum(x => x.Price);
// Call a method passed as a delegate
mentionDiscount(subtotal);
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
Creating a Method to Pass to the Delegate:
A method matching the delegate's signature is created in the Program class.
private static void SubtotalAlert(decimal subtotal)
{
Console.WriteLine($"The subtotal is {subtotal:C2}");
}
Calling the GenerateTotal Method:
The method is passed to the GenerateTotal method via the delegate.
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal(SubtotalAlert):C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
}
Tim runs the application to demonstrate how the delegate works. The console output shows the subtotal and the total for the cart, indicating that the SubtotalAlert method was successfully passed and executed via the delegate.

Tim Corey then explores the use of func and action delegates in C#. These are special types of delegates provided by Microsoft to simplify delegate usage with generics.
Tim highlights a common problem: hard-coded discount logic in the GenerateTotal method. This approach is inflexible and requires code changes, recompilation, and redeployment whenever discount rules change.
public decimal GenerateTotal()
{
decimal subtotal = Items.Sum(x => x.Price);
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}Public Function GenerateTotal() As Decimal
Dim subtotal As Decimal = Items.Sum(Function(x) x.Price)
If subtotal > 100 Then
Return subtotal * 0.80D
ElseIf subtotal > 50 Then
Return subtotal * 0.85D
ElseIf subtotal > 10 Then
Return subtotal * 0.90D
Else
Return subtotal
End If
End FunctionTim introduces the func delegate to address the hard-coded discount problem. The func delegate is a generic delegate that represents a method signature with a return type and up to 16 input parameters.
Defining the Func Delegate:
The func delegate is used in the GenerateTotal method to handle discount calculations dynamically.
public decimal GenerateTotal(Func<List<ProductModel>, decimal, decimal> calculateDiscountedTotal)
{
decimal subtotal = Items.Sum(x => x.Price);
MentionDiscount(subtotal);
return calculateDiscountedTotal(Items, subtotal);
}
Creating the Discount Calculation Method:
A method matching the func delegate's signature is created in the Program class.
private static decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
Passing the Method to the Func Delegate:
The CalculateLevelDiscount method is passed to the GenerateTotal method via the func delegate.
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal(CalculateLevelDiscount):C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
private static decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
}
Tim demonstrates the modified application, showing that it functions correctly and dynamically calculates the discount based on the provided logic.

Tim compares the custom delegate and func delegate:
Both approaches offer flexibility, but the choice depends on the specific use case and complexity of the application.
Tim Corey addresses a common question about the use of delegates: Why have a delegate if all the work seems to be done elsewhere?
Tim explains that the purpose of delegates is to provide flexibility and extensibility in the code. The GenerateTotal method in the ShoppingCartModel class might do more than just calculate discounts. It might also handle tasks like checking stock availability, validating cart contents, or other business logic. Delegates allow you to pass in specific methods for unique tasks or custom behavior without changing the core method. This makes the code more modular and easier to maintain.
Delegates are especially useful in scenarios where you want to:
Tim introduces the Action delegate, another special type of delegate in C#. The Action delegate is similar to Func, but it doesn't return a value (i.e., it returns void).
Creating the Action Delegate:
Define the Action delegate in the GenerateTotal method to handle alerts or messages.
public decimal GenerateTotal(Func<List<ProductModel>, decimal, decimal> calculateDiscountedTotal, Action<string> tellUserWeAreDiscounting)
{
decimal subtotal = Items.Sum(x => x.Price);
MentionSubtotal(subtotal);
tellUserWeAreDiscounting("We are applying your discount.");
return calculateDiscountedTotal(Items, subtotal);
}
Creating the Alert Method:
Define a method to match the Action delegate's signature.
private static void AlertUser(string message)
{
Console.WriteLine(message);
}
Passing the Method to the Action Delegate:
Pass the AlertUser method to the GenerateTotal method via the Action delegate.
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal(CalculateLevelDiscount, AlertUser):C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
private static decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
private static void AlertUser(string message)
{
Console.WriteLine(message);
}
}
Tim shows how to use anonymous methods, allowing you to define methods on the fly without naming them.
Defining Anonymous Methods:
Instead of creating named methods, you can define the methods directly where they are needed.
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal((items, subtotal) =>
{
if (subtotal > 100) return subtotal * 0.80M;
else if (subtotal > 50) return subtotal * 0.85M;
else if (subtotal > 10) return subtotal * 0.90M;
else return subtotal;
},
(message) => Console.WriteLine(message)):C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
}
Understanding the Syntax:
=> operator (lambda expression) to define the method body directly inline.By using delegates, including Func, Action, and anonymous methods, developers can create more dynamic and modular code, allowing for flexible and reusable components.
In this segment, Tim Corey demonstrates the power of delegates by extending their use to a WinForms application. This highlights how delegates can facilitate different behaviors in various user interface (UI) contexts.
WinForm UI with Two Buttons:
public partial class Dashboard : Form
{
ShoppingCartModel cart = new ShoppingCartModel();
public Dashboard()
{
InitializeComponent();
PopulateCartWithDemoData();
}
private void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
private void messageBoxDemoButton_Click(object sender, EventArgs e)
{
decimal total = cart.GenerateTotal(SubtotalAlert, CalculateLevelDiscount, PrintOutDiscountAlert);
MessageBox.Show($"The total is {total:C2}");
}
private void textBoxDemoButton_Click(object sender, EventArgs e)
{
// Code for TextBox demo will go here
}
}Public Partial Class Dashboard
Inherits Form
Private cart As New ShoppingCartModel()
Public Sub New()
InitializeComponent()
PopulateCartWithDemoData()
End Sub
Private Sub PopulateCartWithDemoData()
cart.Items.Add(New ProductModel With {.ItemName = "Cereal", .Price = 3.63D})
cart.Items.Add(New ProductModel With {.ItemName = "Milk", .Price = 2.95D})
cart.Items.Add(New ProductModel With {.ItemName = "Strawberries", .Price = 7.51D})
cart.Items.Add(New ProductModel With {.ItemName = "Blueberries", .Price = 6.75D})
End Sub
Private Sub messageBoxDemoButton_Click(sender As Object, e As EventArgs) Handles messageBoxDemoButton.Click
Dim total As Decimal = cart.GenerateTotal(AddressOf SubtotalAlert, AddressOf CalculateLevelDiscount, AddressOf PrintOutDiscountAlert)
MessageBox.Show($"The total is {total:C2}")
End Sub
Private Sub textBoxDemoButton_Click(sender As Object, e As EventArgs) Handles textBoxDemoButton.Click
' Code for TextBox demo will go here
End Sub
End ClassPrintOutDiscountAlert:
This method will be used to show an alert with the discount information.
private void PrintOutDiscountAlert(string message)
{
MessageBox.Show(message);
}
SubtotalAlert:
This method will display the subtotal in a message box.
private void SubtotalAlert(decimal subtotal)
{
MessageBox.Show($"The subtotal is {subtotal:C2}");
}
CalculateLevelDiscount:
This method will calculate the discount based on the number of items in the cart.
private decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
if (items.Count > 3)
{
return subtotal - 3M;
}
return subtotal - items.Count;
}
Using Delegates in Button Click Event:
The messageBoxDemoButton_Click method demonstrates how to pass the delegates to the GenerateTotal method and handle the results using message boxes.
private void messageBoxDemoButton_Click(object sender, EventArgs e)
{
decimal total = cart.GenerateTotal(SubtotalAlert, CalculateLevelDiscount, PrintOutDiscountAlert);
MessageBox.Show($"The total is {total:C2}");
}
Running the Application:

Tim Corey explains delegates in C# clearly, covering their basics, advanced usage, and practical examples like using delegates in a shopping cart. He shows how delegates enable flexible and reusable code, including Func, Action, and anonymous methods. Watch the full video to learn how to apply delegates effectively in your projects!
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!
Join our newsletter, you’ll get exclusive access on article updates. We value your privacy