Saturday, 27 June 2015

Action Delegate

In a previous posts we talked about leveraging Funcs tor replace delegates that reference functions, we can also you actions to reference methods, the difference between a function and a method is simply that functions return something, whereas methods do not. In short:
  • Func: represents a function
  • Action: represents a method
Actions have a syntax like so Action<T1, T2, T3>(T1 variable1, T2 variable2, T3 variable3), up to 18 parameters last i checked which means that you declare what types your action will consume.

namespace pav.actionExample;

class Program
{

    static void AddMethod(int x, int y)
    {
        Console.WriteLine(x + y);
    }

    static void Main()
    {
        //declared action
        Action<int, int> Add1 = AddMethod;

        //inline action
        Action<int, int> Add2 = (x, y) => { Console.WriteLine(x + y); };

        Add1(2, 3);
        Add2(2, 3);
    }
}

Above there's two actions Add1 and Add2, the difference is that add2 was declared in an inline fashion using the lambda syntax, there really isn't a right or wrong way of doing this, there's many opinions on the matter, mine is simply this, if' the method you're declaring is more than 5 lines don't use the lambda expression. A more generic rule is if it's hard to read, separate it.

Previously we reduced our code using funcs, now we can leverage both funcs and actions to further reduce, simplify and decouple our code. 

namespace pav.funcActionExample;

class Program
{
    struct Employee
    {
        public string FirstName;
        public string LastName;

        public Employee(string FirstName, string LastName)
        {
            this.FirstName = FirstName;
            this.LastName = LastName;
        }
    }

    class EmployeeService
    {
        public List<Employee> Employees { get; } = new List<Employee>() {
                new Employee("Pawel","Chooch"), new Employee("Steve","Smith"),
                new Employee("Ian","Price"), new Employee("Tomek","Wan"),
                new Employee("Jacques","Cocion") };

        public void ProcessEmployee(Func<Employee, string> formatEmployeeFunc, Action<string> printAction)
        {
            if (formatEmployeeFunc is not null && printAction is not null)
                foreach (Func<Employee, string> del in formatEmployeeFunc.GetInvocationList())
                    foreach (var e in Employees)
                        printAction(del(e));
        }
    }

    static void Main()
    {
        var es = new EmployeeService();

        Func<Employee, string> formatFunc = null;

        formatFunc += e => $"{e.FirstName} {e.LastName}";
        formatFunc += e => $"{e.FirstName.Substring(0, 1)}. {e.LastName}";


        es.ProcessEmployee(formatFunc, (string str) => Console.WriteLine(str));
    }
}

as before we've greatly reduced the number of lines, increased our readability and decoupled our output logic from our service, now we pass an inline action " (string str) => Console.WriteLine(str) " which defines how our data will be outputted.