Introduction to Actions

Overview

We already had an introduction to delegates and we saw how important they are in .NET applications. Because the concepts of delegates are very important, valuable, and useful in programming, the .NET library provides a special delegate, that is, pre-built. This delegate is named Action. The Action delegate is provided in many (overloaded) versions so that you can find a syntax that suits any void function or method. This means that in many cases, instead of creating a delegate from scratch, you can use the Action delegate instead.

A Simple Action

As we saw in our introduction, the simplest delegate is a method that doesn't use any parameter. Here is the example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    Observation quote = new Observation(HoldConference);
    
    quote();
}

@functions{
    delegate void Observation();

    // A function for a delegate
    void HoldConference()
    {
    {
        WriteLine("There are known knowns. These are things we know that we know. " +
                  "There are known unknowns. That is to say, there are things that " +
                  "we know we don't know. But there are also unknown unknowns. " +
                  "There are things we don't know we don't know.\n- Donald Rumsfeld -");
    }
}

This would produce:

There are known knowns. These are things we know that we know. There are known unknowns. That is to say, there are things that we know we don't know. But there are also unknown unknowns. There are things we don't know we don't know.
- Donald Rumsfeld -

To support delegates that don't use any parameters, the .NET library provides the following version of the Action delegate:

public delegate void Action()

Based on this version of the Action delegate, you don't have to first create a delegate. In the section where you declare a variable for the delegate, use Action. This can be done as follows:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    Action quote = new Action(HoldConference);
    
    quote();
}

@functions{
    delegate void Observation();

    void HoldConference()
    {
        WriteLine("There are known knowns. These are things we know that we know. " +
                  "There are known unknowns. That is to say, there are things that " +
                  "we know we don't know. But there are also unknown unknowns. " +
                  "There are things we don't know we don't know.\n- Donald Rumsfeld -");
    }
}

If you declare the variable starting with the Action keyword, you don't need that Action keyword after the new keyword. This can be done as follows:

@{
    Action quote = new(HoldConference);
}

@functions{
    void HoldConference()
    {
    }
}

On the other hand, if you are initializing the variable using with the new Action expression, you can declare the variable using the dynamic or the var keyword. Here are examples:

@{
    var quote = new Action(HoldConference);
    dynamic say = new Action(HoldConference);
}

@functions{
    void HoldConference()
    {
    }
}

If you declare the variable using the Action keyword as type, you don't need to use the new operator to initialize the variable. This means that you can omit calling Action() as a constructor. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    Action quote = HoldConference;
    
    quote();
}

@functions{
    delegate void Observation();

    void HoldConference()
    {
        WriteLine("There are known knowns. These are things we know that we know. " +
                  "There are known unknowns. That is to say, there are things that " +
                  "we know we don't know. But there are also unknown unknowns. " +
                  "There are things we don't know we don't know.\n- Donald Rumsfeld -");
    }
}

Actions and Anonymous Methods

Action delegates support anonymous methods. The anonymous delegate is created in the same way done for regular delegates. That is, initialyze the Action variable with the delegate keyword, the parentheses, and the body of the method in curly brackets. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    WriteLine("=================");
    WriteLine("Action Delegates");
    WriteLine("-----------------");
    
    elementaryAlgebra();
    WriteLine("=================");
}

@functions{
    Action elementaryAlgebra = delegate ()
    {
        Random rndNumber = new Random();
        int number1 = rndNumber.Next(0, 20);
        int number2 = rndNumber.Next(0, 20);
        int total = number1 + number2;
        
        WriteLine("{0} + {1} = {2}", number1, number2, total);    
    };
}

Here is an example of running the program:

Action Delegates
-----------------
9 + 12 = 21
=================

Actions and Lambda Expressions

Like regular delegates, actions support lambda expressions. To apply a lambda expression to an action delegate, initialize the Action variable with empty parentheses, the => operator, and the body of the method. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    WriteLine("=================");
    WriteLine("Action Delegates");
    WriteLine("-----------------");
    
    elementaryAlgebra();
    WriteLine("=================");
}

@functions{
    Action elementaryAlgebra = () =>
    {
        Random rndNumber = new Random();
        int number1 = rndNumber.Next(0, 20);
        int number2 = rndNumber.Next(0, 20);
        int total = number1 + number2;
        
        WriteLine("{0} + {1} = {2}", number1, number2, total);    
    };
}

A Delegate With a Parameter

We saw that a delegate can use a parameter. In fact, the Action delegate is created as generic. To support the ability to apply a parameter to a delegate, the .NET library provides an Action delegate that uses the following syntax:

public delegate void Action<in T>(T obj)

The parameter can be any type, such as a primitive type or a string. When declaring the variable, to indicate that the delegate is using a parameter, use Action<>. Include the parameter type between < and >. Here is an example:

@{
    Action<string> quote = new Action<string>(Create);
}

@functions{
    void Create(string say)
    {
        WriteLine(say);
    }
}

You can omit the second Action<string>. Once again, when calling the method, pass an appropriate argument. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    Action<string> quote = new Action<string>(Create);
    
    quote("Les dances à la gloire de la fornication.");
}

@functions{
    void Create(string say)
    {
        WriteLine(say);
    }
}

An Action Delegate With Many Parameters

The Action delegate can use different numbers and types of parameters. To support this, the .NET library provides 17 versions of the Action delegate ranging from one that doesn’t use any parameter to one that takes 16 arguments. To use one of them, declare a variable of type Action<>. Between < and >, enter the types separated by commas. Initialize the variable with a method that use the same number or ordinal types of parameters.

An Action Delegate as a Type

Returning an Action

You can create a method that returns an Action delegate. As seen for formal delegates, when creating the method, set its return type as Action. In the body of the method, do what you want. Before the end of the method, return an object of the Action type. One solution is to declare a variable of Action type and initialize it appropriately. before calling the method, you can declare a variable and assign the Action method to it. Then call that last variable as if it were a method. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    Action operation = Doubler();
    
    operation();
}

@functions{
    Action Doubler()
    {
        Action times = new Action(Multiplication);

        return times;
    }

    void Multiplication()
    {
        Random rndNumber = new Random();
        int number1 = rndNumber.Next(0, 100);
        int number2 = rndNumber.Next(0, 100);
        int total = number1 * number2;

        WriteLine(string.Format("{0} * {1} = {2}", number1, number2, total));
    }
}

In the same way, a method can return a list or an array of Action delegates.

An Action as Parameter

To create a method that uses a parameter of type Action, precede that name of the parameter with Action. Here is an example:

@functions{
    void Create(Action speech)
    {
    }
}

In the same way, as opposed to passing it by itself, an Action delegate can be passed as argument along with other (types of) parameters. In the body of the method, ignore or use the Action parameter. Probably the simplest way to use it is to call it as a method. Here is an example:

@functions{
    void Create(Action speech)
    {
        speech();
    }
}

When calling the method, you can pass the name of a method that uses the same syntax as the type of Action that was passed as parameter (remember that the .NET library provides various syntaxes for actions). Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    Create(Hold);
}

@functions{
    void Hold()
    {
        WriteLine("People who live in glass houses should not throw stones.");
    }

    void Create(Action speech)
    {
        speech();
    }
}

This would produce:

People who live in glass houses should not throw stones.

.NET Function Delegates

Introduction

An Action is a delegate for a method that doesn’t produce a value. A function is an action that produces or returns a value. At a minimum, a function doesn't use any parameter.

Although you can create your own function delegates , to provide you a faster technique, the .NET library provides a special delegate named Func. It uses a generic format as in Func<>.

A Simple Function Delegate

Consider the following code:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    Evaluation eval = new Evaluation(Calculate);
    
    WriteLine(string.Format("Weekly Salary: {0}", eval().ToString("F")));
}

@functions{
    delegate double Evaluation();

    double Calculate()
    {
        double hSalary = 25.75;
        double tWorked = 38.50;

        double wSalary = hSalary * tWorked;
        return wSalary;
    }
}

This would produce:

Weekly Salary: 991.38

A simple function is one that doesn't use any parameter but returns a value. To support such functions, the .NET library provides a Func<> delegate that uses the following syntax:

public delegate TResult Func<out TResult>()

The out TResult expression indicates that the delegate returns a value as TResult. When creating the function or method for the delegate, you must indicate the desired return value. To associate the function to the delegate, declare a Func<> variable. Between < and >, include the same return type as the function. Initialize the variable with the name of the function. You can then call the variable as if it were a method. This can be done as follows:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    Func<double> eval = new Func<double>(Calculate);
    
    string strSalary = string.Format("Weekly Salary: {0:f}", eval());
}

@functions{
    double Calculate()
    {
        double hSalary = 15.95;
        double tWorked = 39.00;

        return hSalary * tWorked;
    }
}

<pre>======================
@strSalary
======================</pre>

In the above code, we used the new operator to initialize the variable. You can omit it. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@{
    Func<double> eval = Calculate;
    
    string strSalary = string.Format("Weekly Salary: {0:f}", eval());
}

@functions{
    double Calculate()
    {
        double hSalary = 15.95;
        double tWorked = 39.00;

        return hSalary * tWorked;
    }
}

<pre>======================
Net Pay:       @strSalary
======================</pre>

Tuples and Function Delegates

We already know that a function that returns one value can return it as a tuple instead. As we know already, tuples make it possible for a function to return many values. This concept also applies to function delegates. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    Func<(string, double, string, double, double)> eval = Calculate;

    WriteLine(string.Format("{0}: {1}\n{2}: {3}\nNet Pay: {4:f}",
        	                eval().Item1,
                            eval().Item2.ToString(),
                            eval().Item3,
                            eval().Item4.ToString("F"),
                            eval().Item5));
}

@functions{
    (string, double, string, double, double) Calculate()
    {
        double hSalary = 27.35;
        double tWorked = 39.50;

        double wSalary = hSalary * tWorked;
        return ("Hourly Salary", hSalary, "Time Worked", tWorked, wSalary);
    }
}

This would produce:

Hourly Salary: 27.35
Time Worked: 39.50
Net Pay: 1080.33

A Simple Anonymous Function Delegate

You can use the delegate keyword to create an anonymous function delegate. The technique is the same we have used so far. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    WriteLine(string.Format("Weekly Salary: {0:f}", eval()));
}

@functions{
    Func<double> eval = delegate()
    {
        double hSalary = 18.85;    
        double tWorked = 41.00;

        return hSalary * tWorked;
    };
}

A Simple Lambda Expression

A function delegate that uses a parameter supports lambda expressions. To implement it, remove the delegate keyword and add the => operator after the parentheses. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    WriteLine(string.Format("Weekly Salary: {0:f}", eval()));
}

@functions{
    Func<double> eval = () =>
    {
        double hSalary = 16.07;    
        double tWorked = 32.50;

        return hSalary * tWorked;
    };
}

A Function Delegate with a Parameter

To support function delegates that use one parameter, the .NET library provides a delegate that uses the following syntax:

public delegate TResult Func<in T, out TResult>(T arg);

This syntax is for a function that takes one argument (and of course returns a value). Here is an example of such a function (and how to access it):

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    Operation oper = Multiply;
    
    double dailySalary = EvaluateSalary(oper);
    
    WriteLine(string.Format("Daily Salary: {0:f}", dailySalary));
}

@functions{
    double hourlySalary = 12.75;

    delegate double Operation(double a);

    double Multiply(double timeWorked)
    {
        if (timeWorked <= 8)
        {
            return hourlySalary * timeWorked;
        }
        else
        {
            double regularPay = 8.00 * hourlySalary;
            double overtimePay = (timeWorked - 8.00) * hourlySalary * 1.50;

            return regularPay + overtimePay;
        }
    }

    // A function for a delegate
    double EvaluateSalary(Operation calc)
    {
        return calc(hourlySalary);
    }
}

This would produce:

Daily Salary: 192.84

Because the above function included many lines of code, we created a body for it and delimited it with curly brackets. If a function includes a single line of code, it doesn't need a body. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    double hourlySalary = 16.55;
    
    Func<double, double> Calculate = timeWorked => hourlySalary * timeWorked;
    
    double dailySalary = Calculate(12.00);
    
    WriteLine(string.Format("Daily Salary: {0:f}", dailySalary));
}

This would produce:

Daily Salary: 198.60

A Function Delegate as Type

Returning a Function Delegate

A function delegate is a type, like a value-type or a class/record/structure. As such, you can create a function or method that returns a function. To do this, specify Func<> as the return type. The parameter type can be a value type. Here is an example:

@functions{
    public Func<double> Calculate()
    {
     	. . .
    }
}

Do whatever you want in the function or method but before it ends, return a value of type Func<>. Here is an example:

@functions{
    Func<int> Calculate()
    {
        Func<int> result = Addition;

        return result;
    }

    int Addition()
    {
        Random rndNumber = new Random();
        int number1 = rndNumber.Next(0, 20);
        int number2 = rndNumber.Next(0, 20);
        int total = number1 + number2;

        return total;
    }
}

You can also just return the name of the function or method that returns the function delegate. Here is an example:

@functions{
    public Func<int> Calculate()
    {
        return Addition;
    }

    private int Addition()
    {
        Random rndNumber = new Random();
        int number1 = rndNumber.Next(0, 20);
        int number2 = rndNumber.Next(0, 20);
        int total = number1 + number2;

        return total;
    }
}

When calling the function or method, you can assign it to a variable of type Func<>. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    Func<int> eval = Calculate();

    WriteLine(string.Format("Daily Salary: {0}", eval()));
}

@functions{
    Func<int> Calculate()
    {
        Func<int> result = Addition;

        return result;
    }

    int Addition()
    {
        Random rndNumber = new Random();
        int number1 = rndNumber.Next(0, 20);
        int number2 = rndNumber.Next(0, 20);
        int total = number1 + number2;

        return total;
    }
}

This would produce:

Daily Salary: 31

A Function Delegate as Parameter With One Parameter

We already know that you can pass a function as argument if you use a delegate. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    double area = Calculate(Multiply);

    WriteLine(string.Format("Area of Cube: {0}", area));
}

@functions{
    delegate double Operation(double a);

    double value = 148.86;

    double Multiply(double side)
    {
        return side * side;
    }

    double Calculate(Operation oper)
    {
        return oper(value);
    }
}

This would produce:

Area of Cube: 22159.299600000006

When creating the function or method, instead of your own function, you can use Func<>. Here is an example for a function that takes one argument. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    double area = Calculate(Multiply);

    WriteLine(string.Format("Area of Cube: {0}", area));
}

@functions{
    double value = 148.86;

    double Multiply(double side)
    {
        return side * side;
    }

    double Calculate(Func<double, double> oper)
    {
        return oper(value) * 6.00;
    }
}

This would produce:

Area of Cube: 132955.79760000005

Calling a One-Parameter Function Locally

When calling the method, instead of first creating a method that would be passed as argument, you can define the method directly where it is needed. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    double area = Calculate((double side) =>
        {
            return side * side;
        });

    WriteLine(string.Format("Area of Cube: {0}", area));
}

@functions{
    double value = 148.86;

    double Calculate(Func<double, double> oper)
    {
        return oper(value) * 6.00;
    }
}

Remember that you can omit the data type(s) of the parameter(s). Also remember that if the method is using one parameter, the parameter doesn't need parentheses. And if the method contains a single statement, it doesn't need a body. Here is an example:

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    double area = Calculate(side => side * side);

    WriteLine(string.Format("Area of Cube: {0}", area));
}

@functions{
    double value = 148.86;

    double Calculate(Func<double, double> oper)
    {
        return oper(value) * 6.00;
    }
}

A Function Delegate with Two Parameters

Depending on your project, you may need a function that uses two parameters. To support this concept for function delegates, the .NET library provides the following version of the Func<> delegate:

public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);

This generic delegate uses three parameter types. The last parameter type,TResult, is the return type of the function. The first two types represent the data types of the parameters. In a simple version, they can be value types (number, character, Boolean) or a string. They can be the same or different types.

As seen with function delegates of one parameter, you can create a method that takes one parameter, you can create a method that takes a function of two parameters and, in the method, simply call the function and pass two arguments. You can use arguments you also pass to the method. Here is an example:

@functions{
    double EvaluateSubTotal(Func<int, double, double> calc, int qty, double amount)
    {
        return calc(qty, amount);
    }
}

In this case, when calling the method, in the placeholder of the function passed as parameter, type the parentheses. In the parentheses, you must include two arguments separated by a comma. Outside the parentheses, type => followed by the desired operation for the arguments. Here is an example:

@functions{
    double subTotalShirtsSmall = hc.EvaluateSubTotal((q, a) => q * a,
						 iQuantityShirtsSmall, dUnitPriceShirtsSmall);
}

Alternatively, you can perform the operation in the method that uses the function delegate. Here is an example:

@functions{
    double EvaluateSubTotal(Func<int, double, double> calc, int qty, double amount)
    {
        return qty * amount;
    }
}

Alternatively, you can define a function delegate where it is needed, then call it. Here is an example

@page
@model Exercises.Pages.ExerciseModel
@using static System.Console
@{
    double subTotal = 2425.75;
    int shippingAndHanling = 36;

    Func<double, double, double> Plus = (a, b) => a + b;
    double invoiceTotal = Plus(subTotal, shippingAndHanling);

    WriteLine(string.Format("Invoice Total: {0}", invoiceTotal));
}

This would produce:

Invoice Total: 2461.75

A .NET Function Delegate With Many Parameters

To support function delegates that can use various numbers and types of parameters, the .NET library provides 17 versions of the Func<> delegates. One version is for a function that uses no parameter. The other versions are for functions that take 1 to 16 arguments. The syntaxes of the function delegates are:

public delegate TResult Func<out TResult>();
public delegate TResult Func<in T, out TResult>(T arg);
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
public delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3);
public delegate TResult Func<in T1, in T2, in T3, in T4, out TResult>(	T1 arg1, T2 arg2, T3 arg3, T4 arg4);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, out TResult>(	T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11,  T12 arg12);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16);

As we saw already, for the first syntax, TResult is the only factor and it represents the return value. For the other syntaxes, the last parameter, TResult, is the return value. This means that, when declaring the Func<> variable, between < and >, start with the type(s) of the parameter(s) and end with the return type.

Everyone of these functions can be passed as argument or returned from a method.


Previous Copyright © 2008-2022, FunctionX Thursday 21 October 2021 Next