Руководство C# Delegate и Event

1- What is Delegate?

В C# каждая функция (формула, или constructor) имеет вид функции. Посмотрим на метод  SayHello ниже:
Полный пример:
HelloProgram.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDelegatesTutorial
{
    class HelloProgram
    {
        // This is the method that has a parameter of type string and returns the string
        // Function type: (string) -> (string)
        public string SayHello(string name)
        {
            return "Hello " + name;
        }
        
        // This is the method that has 2 parameter and returns the string
        // Function type:  (string, string) -> (string)
        public string SayHello(string firstName, string lastName)
        {
            return "Hello " + firstName + " " + lastName;
        }

        // This is the method that has a parameter and returns nothing.
        // Function type: (string) -> ()
        public void Silent(string name)
        {

        }
    }


}
Два метода ниже оба имеют одинаковый тип функции: 
C# использует ключевое слово  delegate (Делегат) чтобы дать определения для функций (метод или constructor) имеющие один вид функции.
Синтаксис:
// Syntax to define a delegate:

delegate <return_type> <delegate_name> <parameter_list>
Пример:
// Define a type representing the function type:
// (string,string) -> (string).

private delegate string MyDelegate(string s1, string s2);

2- Delegate examples

MathUtils.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDelegatesTutorial
{
    class MathUtils
    {

        // (int, int)  -> (int)
        public static int sum(int a, int b)
        {
            return a + b;
        }

        // (int, int)  -> (int)
        public static int minus(int a, int b)
        {
            return a - b;
        }

        // (int, int)  -> (int)
        public static int multiple(int a, int b)
        {
            return a * b;
        }
 

    }

}
Пример внизу определяет delegate IntIntToInt представляющий функиции как  (int, int) -> (int).
MyFirstDelegate.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDelegatesTutorial
{
    class MyFirstDelegate
    {
        // Defining a 'delegate' for the function type:
        // (int, int) -> (int)
        delegate int IntIntToInt(int a, int b);



        public static void Main(string[] args)
        {

            // Create a delegate object
            // Pass a function to it. (Same function type with delegate)
            IntIntToInt iiToInt = new IntIntToInt(MathUtils.sum);

            // Execute a delegate
            // It will call the function (or method) that it represents.
            int value = iiToInt(10, 20); // 30

            Console.WriteLine("Value = {0}", value);

            // Assign other values for delegate
            iiToInt = new IntIntToInt(MathUtils.multiple);

            value = iiToInt(10, 20); // 200

            Console.WriteLine("Value = {0}", value);

            Console.Read();

        }


    }


}
Running the example:
Вы так же можете создать  delegate представляющая нестатическую функцию (none static). Например:
HelloProgramTest.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDelegatesTutorial
{
    class HelloProgramTest
    {
        // Create a Delegate
        // Representing the type of function: (string) -> (string).
        private delegate string StringStringToString(string s);



        public static void Main(string[] args)  
        {

            // Create HelloProgram object.
            HelloProgram program = new HelloProgram();

            // Create a delegate, represent for sayHello function
            StringStringToString ssToS = new StringStringToString(program.SayHello);


            // Test
            string greeting = ssToS("Tom");

            Console.WriteLine(greeting);

            Console.Read();
        }

    }


}

3- Function returns a function

В C#, с помощью Delegate вы можете создать функцию возвращающую функцию (На самом деле функция возвращает Delegate).
 
TaxFormulas.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDelegatesTutorial
{
    class TaxFormulas
    {
        // A delegate representing the type of function: (float) -> (float).
        public delegate float TaxFormula(float salary);

        // US tax calculation formula (10% of salary).
        public static float UsaFormula(float salary)
        {
            return 10 * salary / 100;
        }

        // Vietnam tax calculation formula (5% of salary).
        public static float VietnamFormula(float salary)
        {
            return 5 * salary / 100;
        }
        // Default tax calculation formula (7% of salary).
        public static float DefaultFormula(float salary)
        {
            return 7 * salary / 100;
        }

        // Returns a function to calculate the tax, based on the national code (VN,USA,..)
        public static TaxFormula GetSalaryFormula(string countryCode)
        {
            if (countryCode == "VN")
            {
                return TaxFormulas.VietnamFormula;
            }
            else if (countryCode == "USA")
            {
                return TaxFormulas.UsaFormula;
            }
            return TaxFormulas.DefaultFormula;
        }

    }

}
TaxFormulaTest.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDelegatesTutorial
{
    class TaxFormulaTest
    {


        public static void Main(string[] args)
        {
            float salary = 1000f;

            // The formula for calculating tax in Vietnam.
            TaxFormulas.TaxFormula formula = TaxFormulas.GetSalaryFormula("VN");

            float tax = formula(salary);

            Console.WriteLine("Tax in Vietnam = {0}", tax);

            // The formula for calculating tax in Canada.
            formula = TaxFormulas.GetSalaryFormula("CA");

            tax = formula(salary);

            Console.WriteLine("Tax in Canada = {0}", tax);

            Console.Read();
        }
    }

}
Запуск примера:

4- Anonymous methods

Из Delegate вы можете создать анонимный метод (Anonymous method), который не имеет имени, только код (body) метода, это блок (block) с 0 и больше параметрами, и разные способы возвращения.
AnonymousMethod.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDelegatesTutorial
{
    class AnonymousMethod
    {
        // A delegate representing the type of function: (float) -> (float).
        // Tax calculation based on salary.
        public delegate float TaxFormula(float salary);


        public static TaxFormula GetTaxFormula(string countryCode)
        {
            if ("USA" == countryCode)
            {
                TaxFormula usaFormula = delegate(float salary)
                {
                    return 10 * salary / 100;
                };
                return usaFormula;
            }
            else if ("VN" == countryCode)
            {
                TaxFormula vnFormula = delegate(float salary)
                {
                    return 5 * salary / 100;
                };
                return vnFormula;
            }
            return delegate(float salary)
            {
                return 7 * salary / 100;
            };
        }


        public static void Main(string[] args)
        {
            string countryCode = "VN";
            float salary = 1000;

            TaxFormula formula = GetTaxFormula(countryCode);

            float tax = formula(salary);

            Console.WriteLine("countryCode = {0}, salary = {1} -> tax = {2}"
                                    , countryCode, salary, tax);

            Console.Read();
        }
    }

}
Запуск:

5- Multicasting of a Delegate

C# позволяет вам добавить (+) два Delegate чтобы создать новый Delegatе. Заметьте, что добавленные Delegate должны иметь один тип функции,и в функции без возвращения. Когда выполняется новый delegate, остальные  под -delegate тоже будут выполнены. Это называется  Multicasting для delegate.
Greetings.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDelegatesTutorial
{
    class Greetings
    {
        // Function type:  (String) -> ()
        public static void Hello(String name)
        {
            Console.WriteLine("Hello " + name);
        }

        // Function type:  (String) -> ()
        public static void Bye(string name)
        {
            Console.WriteLine("Bye " + name);
        }

        // Function type:  (String) -> ()
        public static void Hi(string name)
        {
            Console.WriteLine("Hi " + name);
        }
    }

}
MulticastingTest.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDelegatesTutorial
{
    class MulticastingTest
    {
        // Declare a delegate

        public delegate void Greeting(string name);


        public static void Main(string[] args)
        {
            // Create delegate objects.
            Greeting hello = new Greeting(Greetings.Hello);
            Greeting bye = new Greeting(Greetings.Bye);
            Greeting hi = new Greeting(Greetings.Hi);
            
            // Create a delegate (sum of two delegate).

            Greeting greeting = hello + bye;
           
            // You are also using += operator.
            greeting += hi;

            // Gọi thực thi greeting
            greeting("Tom");

            Console.Read();


        }
    }

}
Running the example:

6- What is Event?

В C#, Event является специальным объектом в Delegate, это место хранения методов, эти методы будут выполнены одновременно при происхождении события. Вы можете добавить выполняющие методы в объект Event объекта создающего событие.

Example:

Класс  Button стимулирует кнопку, она означает одно Event (событие) для оповещения при нажатии (Click). Когда нажимается button, event выполняется. Вам нужно добавить методы выполнения для объекта Event снаружи.
Button.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpEventsTutorial
{
   class Button
   {

       private string label;

       public delegate void ClickHander(Button source, int x, int y);
       
       // Define an event, It is not assigned value
       // Its value is assigned on the outside.
       public event ClickHander OnButtonClick;

       public Button(string label)
       {
           this.label = label;
       }

       // Called when button clicked.
       // Locate the x, y where users click.
       public void Clicked()
       {
           Random random = new Random();

           // Random value 1 -> 100
           int x = random.Next(1, 100);

           // Random value 1 -> 20
           int y = random.Next(1, 20);

           // Fire event
           if (OnButtonClick != null)
           {
               OnButtonClick(this, x, y);
           }
         
       }
   }


}
Класс  MyApplication стимулирует приложение с 2-мя кнопками, "Open File" и "Save File".

Вам нужно написать метод сделать что-то при нажатии пользователя в  "Open File", добавить данный метод в событие  OnButtonClick кнопки  openButton

Вам нужно написать метод сделать что-то при нажатии пользователя в  "Save File", vдобавить данный метод в событие  OnButtonClick кнопки  saveButton.
MyApplication.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpEventsTutorial
{
   class MyApplication
   {
       private Button openButton;
       private Button saveButton;
       private string fileName;
      
       // Simulating an application with the buttons.
       public MyApplication()
       {
           // Add a button to interface
           this.openButton = new Button("Open File");

           // Add a button to interface
           this.saveButton = new Button("Save File");

           // Add method to event of button. ('Open Button')
           // (Multicasting feature of Delegate)
           this.openButton.OnButtonClick += this.OpenButtonClicked;

           // Add method to event of button ('Save Button').
           // (Multicasting feature of Delegate)
           this.saveButton.OnButtonClick += this.SaveButtonClicked;
       }

       private void OpenButtonClicked(Button source, int x, int y)
       {
           // Simulation opens a window to select the file.
           Console.WriteLine("Open Dialog to Select a file");
           //
           this.fileName = "File" + x + "_" + y+".txt";
           Console.WriteLine("Openning file: "+ this.fileName);
       }

       private void SaveButtonClicked(Button source, int x, int y)
       {
           if(this.fileName== null)  {
               Console.WriteLine("No file to save!");
               return;
           }    
           // Save file
           Console.WriteLine("Saved file: " + this.fileName);
       }


       public static void Main(string[] args)
       {
           // Open app
           MyApplication myApp = new MyApplication();

           Console.WriteLine("User Click on Open Button ....");

           // Click openButton
           myApp.openButton.Clicked();

           Console.WriteLine("\n\n");
           Console.WriteLine("User Click on Save Button ....");

           // Click saveButton.
           myApp.saveButton.Clicked();


           Console.Read();
         
       }
   }

}
 
Running the example: