A delegate is a reference type, type-safe function(method) pointer. Delegates are used to invoke the method through the delegate instance. To invoke method, delegates methods signature and return type must be same as that of a delegate.
You declare delegate similar to declaring the method:
1 |
public delegate void MyFirstDelegate(string parameterName); |
How do we use delegates?
Lets create simple console application that will use method to display value:
1 2 3 4 5 6 7 8 9 10 11 12 |
class App { static void Main() { PrintMessage("Hello World!"); } public static void PrintMessage(string message) { Console.WriteLine(message); } } |
This class does its intended yob nicely, but we can expand it by adding delegate. First we must declare delegate outside our class and instantiate our newly created delegate inside Main method then we can use our delegate to pass the value to our function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public delegate void MyFirstDelegate(string parameterName); class App { static void Main() { MyFirstDelegate del = new MyFirstDelegate(PrintMessage); del("Hello World!"); } public static void PrintMessage(string message) { Console.WriteLine(message); } } |
Q: This look very complicated process just to invoke function. Why would i ever need to use delegates?
A: Yes it takes some extra work to create delegates but in next example you will see where delegates make much more sense.
Why do we use delegates?
Imagine that you need to create application for HR department and in that application you need to predict salary increase for employees that are employed full-time in that company. You would create application that behaves like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
class App { static void Main() { List<Employee> listOfEmployees = new List<Employee>(); listOfEmployees.Add(new Employee() { Id = 1, FirstName = "John", LastName = "Doe", Salary = 5000.68, YearsEmployed = 3, IsFullTime = true}); listOfEmployees.Add(new Employee() { Id = 2, FirstName = "Jane", LastName = "Doe", Salary = 6990.11, YearsEmployed = 1, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 3, FirstName = "Tommy", LastName = "Atkins", Salary = 4500, YearsEmployed = 4, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 4, FirstName = "Richard", LastName = "Roe", Salary = 4500, YearsEmployed = 1, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 5, FirstName = "Janie ", LastName = "Smith", Salary = 10025.33, YearsEmployed = 6, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 6, FirstName = "John", LastName = "Doe", Salary = 26500.83, YearsEmployed = 2, IsFullTime = true }); Employee printEmployees = new Employee(); printEmployees.NextYearSalary(listOfEmployees); } } public class Employee { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public double Salary { get; set; } public int YearsEmployed { get; set; } public bool IsFullTime { get; set; } public void NextYearSalary(List<Employee> employees) { foreach (var employee in employees) { if (employee.IsFullTime) { double salaryMultiplyer = employee.YearsEmployed * 0.01; double expectedSalary = employee.Salary * salaryMultiplyer; Console.WriteLine(string.Format("Expected salary increase for employee: {0} {1} is: {2}$", employee.FirstName, employee.LastName, expectedSalary)); } } } } |
Here we have Employee class that has necessary properties and NextYearSalary method. NextYearSalary that takes List of Employees and checks each employee for his IsFullTime status, if that status is true then it multiplies employees years of service in company with 1% then multiplies it again with current salary. That way we get potential salary increase and print it to the console. We compile our program and ship it to all companies that use our application.
After some time we receive request from another company. Other company wants to see potential salary increase not for its full time employees but for those employees who have worked there for at least 3 years.
We could expand NextYearSllary method to accommodate that request or create another method only for that company, but these two approaches are not good solution for our problem because we always tend to have clean code and because second approach breaks DRY principle. Instead we will use delegates:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
public delegate bool SalaryIncreaseDelegate(Employee empl); class App { static void Main() { List<Employee> listOfEmployees = new List<Employee>(); listOfEmployees.Add(new Employee() { Id = 1, FirstName = "John", LastName = "Doe", Salary = 5000.68, YearsEmployed = 3, IsFullTime = false }); listOfEmployees.Add(new Employee() { Id = 2, FirstName = "Jane", LastName = "Doe", Salary = 6990.11, YearsEmployed = 3, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 3, FirstName = "Tommy", LastName = "Atkins", Salary = 4500, YearsEmployed = 3, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 4, FirstName = "Richard", LastName = "Roe", Salary = 4500, YearsEmployed = 3, IsFullTime = false }); listOfEmployees.Add(new Employee() { Id = 5, FirstName = "Janie ", LastName = "Smith", Salary = 10025.33, YearsEmployed = 3, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 6, FirstName = "John", LastName = "Doe", Salary = 26500.83, YearsEmployed = 3, IsFullTime = true }); SalaryIncreaseDelegate potentialSalaryIncrease = new SalaryIncreaseDelegate(PotentialSalaryIncreaseForFullTimeEmployees); Employee printEmployees = new Employee(); printEmployees.NextYearSalary(listOfEmployees, potentialSalaryIncrease); } public static bool PotentialSalaryIncreaseForFullTimeEmployees(Employee empl) { if (empl.IsFullTime) return true; else return false; } } public class Employee { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public double Salary { get; set; } public int YearsEmployed { get; set; } public bool IsFullTime { get; set; } public void NextYearSalary(List<Employee> employees, SalaryIncreaseDelegate salaryIncreaseDelegate) { foreach (var employee in employees) { if (salaryIncreaseDelegate(employee)) { double salaryMultiplyer = employee.YearsEmployed * 0.01; double expectedSalary = employee.Salary * salaryMultiplyer; Console.WriteLine(string.Format("Expected salary increase for employee: {0} {1} is: {2}$", employee.FirstName, employee.LastName, expectedSalary)); } } } } |
First wee took a look in NextYearSalary method. Inside our foreach loop we found if statement that needed to be changed. If statement is a boolean and checks Employee object so we created delegate that returns bool value and takes Employee object as a parameter. Now we needed to create method that matches that signature, so we created PotentialSalaryIncreaseForFullTimeEmployees method in our Main method. Next step was to expand signature of NextYearSalary method with SalaryIncreaseDelegate delegate and replace condition of if statement with our delegates variable. Lastly, we call of NextYearSalary method in Main method with missing delegate variable.
Exercise 1:
Try to create method that:
- can be used for SalaryIncreaseDelegate
- can decide which Employees next years potential salaries will be displayed using NextYearSalary method for those employees that have worked in company at least 3 years.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
public delegate bool SalaryIncreaseDelegate(Employee empl); class App { static void Main() { List<Employee> listOfEmployees = new List<Employee>(); listOfEmployees.Add(new Employee() { Id = 1, FirstName = "John", LastName = "Doe", Salary = 5000.68, YearsEmployed = 3, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 2, FirstName = "Jane", LastName = "Doe", Salary = 6990.11, YearsEmployed = 1, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 3, FirstName = "Tommy", LastName = "Atkins", Salary = 4500, YearsEmployed = 4, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 4, FirstName = "Richard", LastName = "Roe", Salary = 4500, YearsEmployed = 1, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 5, FirstName = "Janie ", LastName = "Smith", Salary = 10025.33, YearsEmployed = 6, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 6, FirstName = "John", LastName = "Doe", Salary = 26500.83, YearsEmployed = 2, IsFullTime = true }); SalaryIncreaseDelegate potentialSalaryIncrease = new SalaryIncreaseDelegate(PotentialSalaryIncreaseForEmployeesWith3YearsOfService); Employee printEmployees = new Employee(); printEmployees.NextYearSalary(listOfEmployees, potentialSalaryIncrease); } public static bool PotentialSalaryIncreaseForFullTimeEmployees(Employee empl) { if (empl.IsFullTime) return true; else return false; } public static bool PotentialSalaryIncreaseForEmployeesWith3YearsOfService(Employee empl) { if (empl.YearsEmployed >= 3) return true; else return false; } } public class Employee { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public double Salary { get; set; } public int YearsEmployed { get; set; } public bool IsFullTime { get; set; } public void NextYearSalary(List<Employee> employees, SalaryIncreaseDelegate salaryIncreaseDelegate) { foreach (var employee in employees) { if (salaryIncreaseDelegate(employee)) { double salaryMultiplyer = employee.YearsEmployed * 0.01; double expectedSalary = employee.Salary * salaryMultiplyer; Console.WriteLine(string.Format("Expected salary increase for employee: {0} {1} is: {2}$", employee.FirstName, employee.LastName, expectedSalary)); } } } } |
Exercise 2:
Try to create delegate that will emulate NextYearSalary logic for expected salary:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
public delegate bool SalaryIncreaseDelegate(Employee empl); public delegate double ExpectedSalaryDelegate(Employee empl); class App { static void Main() { List<Employee> listOfEmployees = new List<Employee>(); listOfEmployees.Add(new Employee() { Id = 1, FirstName = "John", LastName = "Doe", Salary = 5000.68, YearsEmployed = 3, IsFullTime = false }); listOfEmployees.Add(new Employee() { Id = 2, FirstName = "Jane", LastName = "Doe", Salary = 6990.11, YearsEmployed = 3, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 3, FirstName = "Tommy", LastName = "Atkins", Salary = 4500, YearsEmployed = 3, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 4, FirstName = "Richard", LastName = "Roe", Salary = 4500, YearsEmployed = 3, IsFullTime = false }); listOfEmployees.Add(new Employee() { Id = 5, FirstName = "Janie ", LastName = "Smith", Salary = 10025.33, YearsEmployed = 3, IsFullTime = true }); listOfEmployees.Add(new Employee() { Id = 6, FirstName = "John", LastName = "Doe", Salary = 26500.83, YearsEmployed = 3, IsFullTime = true }); SalaryIncreaseDelegate potentialSalaryIncrease = new SalaryIncreaseDelegate(PotentialSalaryIncreaseForFullTimeEmployees); ExpectedSalaryDelegate expectedSalaryDelegate = new ExpectedSalaryDelegate(ExpectedSalaryForEmployee); Employee printEmployees = new Employee(); printEmployees.NextYearSalary(listOfEmployees, potentialSalaryIncrease, expectedSalaryDelegate); } public static bool PotentialSalaryIncreaseForFullTimeEmployees(Employee empl) { if (empl.IsFullTime) return true; else return false; } public static double ExpectedSalaryForEmployee(Employee empl) { double salaryMultiplyer = empl.YearsEmployed * 0.01; return empl.Salary * salaryMultiplyer; } } public class Employee { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public double Salary { get; set; } public int YearsEmployed { get; set; } public bool IsFullTime { get; set; } public void NextYearSalary(List<Employee> employees, SalaryIncreaseDelegate salaryIncreaseDelegate, ExpectedSalaryDelegate expectedSalaryDelegate) { foreach (var employee in employees) { if (salaryIncreaseDelegate(employee)) { double expectedSalary = expectedSalaryDelegate(employee); Console.WriteLine(string.Format("Expected salary increase for employee: {0} {1} is: {2}$", employee.FirstName, employee.LastName, expectedSalary)); } } } } |
Continue to Delegates part 2