Detailed use of Action Delegate in C#

In C#, the Action delegate type signifies a method that can be called but does not yield a return value, as its return type is void. It is capable of accepting zero or more input parameters.

Action resides within the System namespace and is available in various overloads to accommodate different quantities of parameters. It is frequently utilized for passing methods as arguments, establishing callbacks, or creating event handlers.

Action Syntax

  • No Parameters: An Action can be defined as a method that does not accept any parameters and returns no value.
    Action myAction = () => Console.WriteLine("Hello, World!");
    myAction();  // Output: Hello, World!
    
  • One or More Parameters: It is possible to create an Action that accepts one or more parameters while still returning no value.
    Action<string> greetAction = (name) => Console.WriteLine($"Hello, {name}!");
    greetAction("Alice");  // Output: Hello, Alice!
    
    Action<int, int> addAction = (x, y) => Console.WriteLine(x + y);
    addAction(5, 10);  // Output: 15
    

Key Features

  • Absence of Return Value: In contrast to Func<T>, the Action delegate does not yield a return value; it consistently returns void.
  • Capability for Multiple Parameters: Action<T1, T2, ..., T16> allows for the definition of a delegate that can accept as many as 16 parameters.
  • Utilization of Anonymous Methods and Lambda Expressions: Action is frequently employed alongside lambda expressions or anonymous methods, promoting more succinct code.
  • Application in Event Handlers: Action proves beneficial when it is necessary to transfer methods (including parameters), particularly for callbacks or event-handling scenarios.

Comparison with Func

  • Action<T>: Used when the method returns void and can take zero or more parameters.
  • Func<T, TResult>: Used when the method returns a value (TResult) and can take zero or more input parameters.

For example

  • Func<int, int, int> could represent a method that takes two integers and returns an integer (like an addition function).
  • Action<int, int> could represent a method that takes two integers and performs an action (like printing the sum), but doesn't return a value.

The appropriate context for utilizing Action

  1. Action is a delegate type used for methods that don't return values.
  2. It is useful for passing methods as parameters or defining callbacks.
  3. You can use Action for methods with zero to 16 input parameters.

Examples of different types of Action delegates

  • Action with No Parameters
    Action action = () => Console.WriteLine("Action with no parameters!");
    action();  
    // Output: Action with no parameters!
    
  • Action with One Parameter
    Action<string> printMessage = (message) => Console.WriteLine(message);
    printMessage("Hello!");
    
    // Output: Hello!
    
  • Action with Multiple Parameters
    Action<int, int> multiplyNumbers = (a, b) => Console.WriteLine(a * b); 
    multiplyNumbers(4, 5);
    
    // Output: 20
    
  • Passing Action as a Parameter to a Method: You can pass an Action as a method parameter to be executed later.
    public void PerformAction(Action action)
    {
        Console.WriteLine("Before action.");
        action();
        Console.WriteLine("After action.");
    }
    // Usage:
    PerformAction(() => Console.WriteLine("Executing action..."));
    // Output:
    // Before action.
    // Executing action...
    // After action
    
  • Using Action with Event Handling
    public class Button
    {
        public Action Click { get; set; }
    
        public void OnClick()
        {
            Click?.Invoke(); // Invoke the Action if it's not null
        }
    }
    Button button = new Button();
    button.Click = () => Console.WriteLine("Button clicked!");
    button.OnClick();
    // Output: Button clicked!
    

Execution of all previously mentioned actions within the application

Step 1. I have developed a WPF user interface, as illustrated below. You may select any application type that aligns with your specific requirements.

WPF

MainWindow.Xaml code

<Window 
    x:Class="ActionDelegateExample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:ActionDelegateExample"
    mc:Ignorable="d"
    Title="MainWindow" 
    Height="450" 
    Width="800">
    
    <Grid>
        <StackPanel Orientation="Vertical">
            <Button 
                x:Name="BtnNoParameter" 
                Content="Action with no Parameter" 
                Click="BtnNoParameter_Click" 
                Height="40" 
                Margin="20"/>
            
            <Button 
                x:Name="BtnWithSingleParameter" 
                Content="Action with single Parameter" 
                Click="BtnWithSingleParameter_Click" 
                Height="40" 
                Margin="20"/>
            
            <Button 
                x:Name="BtnMultipleParameter" 
                Content="Action with multiple Parameter" 
                Click="BtnMultipleParameter_Click" 
                Height="40" 
                Margin="20"/>
            
            <Button 
                x:Name="BtnCallbackParameter" 
                Content="Action with call back" 
                Click="BtnCallbackParameter_Click" 
                Height="40" 
                Margin="20"/>
            
            <Button 
                x:Name="BtnCallEvenHandlingParameter" 
                Content="Action with CallEvenHandling" 
                Click="BtnCallEvenHandlingParameter_Click" 
                Height="40" 
                Margin="20"/>
        </StackPanel>
    </Grid>
</Window>

MainWindow.Xaml.cs code

using System.Windows;

namespace ActionDelegateExample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            EventHandlerManager.OnEventRaised += EventHandlerManager_OnEventRaised;
        }
        private void EventHandlerManager_OnEventRaised()
        {
            MessageBox.Show("Event handled by Subscriber");
        }
        private void BtnNoParameter_Click(object sender, RoutedEventArgs e)
        {
            ActionMethods.NoParameterActionVariable();
        }
        private void BtnWithSingleParameter_Click(object sender, RoutedEventArgs e)
        {
            // Create a RequestMessage
            RequestMessageWithParameter message = new RequestMessageWithParameter 
            { 
                Content = "This is a single parameter request." 
            };
            ActionMethods.ActionWithOneParameter(message);
        }
        private void BtnMultipleParameter_Click(object sender, RoutedEventArgs e)
        {
            // Create a RequestMessage
            RequestMessageWithParameter message = new RequestMessageWithParameter 
            { 
                Content = "This is a multiple parameter request." 
            };

            // Invoke the Action with RequestMessage and an integer
            ActionMethods.ActionWithTwoParameters(message, 42);
        }
        private void BtnCallEvenHandlingParameter_Click(object sender, RoutedEventArgs e)
        {
            EventHandlerManager.RaiseOnEventRaised();
        }
        private void BtnCallbackParameter_Click(object sender, RoutedEventArgs e)
        {
            MessageHandler handler = new MessageHandler();

            // Define the callback action that will handle the RequestMessage after processing
            Action<RequestMessageWithParameter> callback = (request) =>
            {
                MessageBox.Show($"Callback executed. Message content: {request.Content}");
            };
            // Call the method with a message and the callback action
            handler.ProcessRequest("Hello, this is a sample request!", callback);
        }
    }
}

Step 2. Establish a class titled "ActionMethods.cs" as illustrated below. It has been used to define action delegates

using System.Windows;

namespace ActionDelegateExample
{
    public class ActionMethods
    {
        public static Action NoParameterActionVariable = () => NoParameterAction();
        private static void NoParameterAction()
        {
            MessageBox.Show("Action without parameters executed.");
        }
        // Action that accepts one parameter of type RequestMessage
        public static Action<RequestMessageWithParameter> ActionWithOneParameter = (request) =>
        {
            MessageBox.Show(request.Content);
        };
        // Action that accepts two parameters: RequestMessage and an integer
        public static Action<RequestMessageWithParameter, int> ActionWithTwoParameters = (request, count) =>
        {
            MessageBox.Show($"Action with two parameters executed. Message: {request.Content}, Count: {count}");
        };
    }
}

Step 3. Create a class named “MessageHandler.cs” as shown below. It will be used to manage an event of action type.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ActionDelegateExample
{
    public class MessageHandler
    {
        // Method that accepts an Action<RequestMessage> callback
        public void ProcessRequest(string messageContent, Action<RequestMessageWithParameter> callback)
        {
            // Create the RequestMessage
            RequestMessageWithParameter requestMessage = new RequestMessageWithParameter { Content = messageContent };

            // Simulate some processing
            Console.WriteLine("Processing the message...");
            System.Threading.Thread.Sleep(10000);

            // After processing, invoke the callback with the request message
            callback?.Invoke(requestMessage);
        }
    }
}

Step 4. Create a class by the name of ‘EventHandlerManager.cs” to handle the event.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ActionDelegateExample
{
    public class EventHandlerManager
    {
        public static event Action OnEventRaised;
        // Method to raise the event
        public static void RaiseOnEventRaised()
        {
            Action onEventRaised = OnEventRaised;
            if (onEventRaised != null)
            {
                onEventRaised();
            }
        }
    }
}

A detailed description of the aforementioned code

  • RaiseOnEventRaised(): A static method that triggers the OnEventRaised event.
  • Action onEventRaised = OnEventRaised: Creates a local copy of the event delegate. This is done to avoid the potential issues of the event being null while invoking it.
  • if (onEventRaised != null): Checks if there are any subscribers to the event. If there are, it proceeds to invoke the event.
  • onEventRaised(): Invokes the event. This calls all methods that have subscribed to OnEventRaised.

Step 6. The results obtained from testing are.

  • Action with No Parameters
    Parameters
  • Action with One Parameter
    Action
  • Action with Multiple Parameters
     Multiple Parameters
  • Passing Action as a Parameter to a Method(Callback)
    Window
  • Using Action with Event Handling
    Using Action 

Repository Path: https://github.com/OmatrixTech/ActionDelegateExample


Similar Articles