Action
This is a C# function object. Action
objects return no values. The Action
type is similar to a void
method—this generic type is found in the System
namespace.
To specify an Action
, we must have no return value. The Action
must never return a value onto the evaluation stack. Often we use lambdas to specify Actions.
These Actions point to anonymous functions. These functions cannot return values. An Action
instance can receive parameters, but cannot return values.
Action
receives one int
argument when invoked. It returns no result—it is a void
delegate method.Action
receives 2 arguments, named "x" and "y." It prints them out to the console.Action
receives no parameters. So we have a method that receives no arguments, and returns no values.using System; // Version 1: this Action uses one parameter. Action<int> example1 = (int x) => Console.WriteLine("Write {0}", x); example1.Invoke(1); // Version 2: second example uses 2 parameters. Action<int, int> example2 = (x, y) => Console.WriteLine("Write {0} and {1}", x, y); example2.Invoke(2, 3); // Version 3: third example uses no parameter. Action example3 = () => Console.WriteLine("Done"); example3.Invoke();Write 1 Write 2 and 3 Done
How much slower are delegate method calls than direct method calls? To test this, we use the Action
type with a single parameter.
Method1
(which uses no Action
) is called directly 100 million times.Action
instance that points to Method1
is invoked the same number of times.Action
invocation costs more than 3 ns extra each time.using System; using System.Diagnostics; class Program { const int _max = 100000000; static void Main() { // Create Action delegate for Method1. Action<int> action = new Action<int>(Method1); var s1 = Stopwatch.StartNew(); // Version 1: use direct call. for (int i = 0; i < _max; i++) { Method1(5); } s1.Stop(); var s2 = Stopwatch.StartNew(); // Version 2: use Action (delegate) call. for (int i = 0; i < _max; i++) { action.Invoke(5); } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000 * 1000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000 * 1000) / _max).ToString("0.00 ns")); } static void Method1(int param) { // Dummy. if (param == -1) { throw new Exception(); } } }0.32 ns Direct call 3.52 ns Action Invoke call
abstract
Next we benchmark abstract
methods. An Action
int
can be an instance that points to any function that receives an int
parameter.
abstract
class
. We repeatedly call a method on it.Action
instance. The code performs the same number of method calls as version 1.Action
was slower to call than the method from the abstract
class
reference.using System; using System.Diagnostics; abstract class A { public abstract void MethodA(int y); } class AB : A { public override void MethodA(int y) { } } class Program { static void MethodA(int y) { } static void Main() { A abst = new AB(); abst.MethodA(0); Action<int> action = new Action<int>(MethodA); action.Invoke(0); const int max = 100000000; var s1 = Stopwatch.StartNew(); // Version 1: use abstract class method. for (int i = 0; i < max; i++) { abst.MethodA(i); abst.MethodA(i); } s1.Stop(); var s2 = Stopwatch.StartNew(); // Version 2: use Action. for (int i = 0; i < max; i++) { action.Invoke(i); action.Invoke(i); } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / max).ToString("0.00 ns")); } }3.54 ns Abstract call 6.68 ns Action Invoke() call
Dictionary
It is possible to use Action
as the value in a Dictionary
instance. This makes it possible to call functions by a string
key.
static
void
methods based on simple string
keys.Dictionary
of an abstract
class
. Then, instantiate each method as an instance of a derived class
.using System; using System.Collections.Generic; class Program { static void Main() { Dictionary<string, Action> dict = new Dictionary<string, Action>(); dict["cat"] = new Action(Cat); dict["dog"] = new Action(Dog); dict["cat"].Invoke(); dict["dog"].Invoke(); } static void Cat() { Console.WriteLine("CAT"); } static void Dog() { Console.WriteLine("DOG"); } }CAT DOG
The Invoke method receives a number of arguments equal to the specific type of Action
. We must specify these arguments in the Action
type declaration.
What is the difference between Action
and Func
? The Action
type receives parameters but does not return a parameter. Func
receives parameters and returns a result value.
Action
instance never returns anything, while the Func
always returns something.Programs use Actions for many purposes. The Action
type specifies a function object that can receive parameters, but never returns a value onto the stack.