openplanning

Hướng dẫn và ví dụ C# Delegate và Event

  1. Delegate là gì?
  2. Ví dụ với delegate
  3. Hàm trả về một hàm
  4. Phương thức nặc danh
  5. Multicasting của một Delegate
  6. Event là gì

1. Delegate là gì?

Trong C# mỗi một hàm (phương thức, hoặc constructor) đều có một kiểu hàm. Hãy xem phương thức SayHello dưới đây:
Xem ví dụ đầy đủ:
HelloProgram.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDelegatesTutorial
{
    class HelloProgram
    { 
        // Đây là phương thức có một tham số string và trả về string
        // Kiểu hàm:  (string) -> (string)
        public string SayHello(string name)
        {
            return "Hello " + name;
        }
        
        // Đây là một phương thức có 2 tham số string, và trả về string
        // Kiểu hàm: (string, string) -> (string)
        public string SayHello(string firstName, string lastName)
        {
            return "Hello " + firstName + " " + lastName;
        }

        // Đây là một phương thức có 1 tham số, và không trả về gì.
        // Kiểu hàm: (string) -> ()
        public void Silent(string name)
        {

        }
    }


}
Hai phương thức dưới đây có cùng một kiểu hàm:
C# sử dụng từ khóa delegate (Người đại diện) để định nghĩa ra một thực thể đại diện cho các hàm (phương thức, hoặc constructor) có cùng một kiểu hàm.
Cú pháp:
// Cú pháp định nghĩa một delegate:
delegate <return_type> <delegate_name> <parameter_list>
Ví dụ:
// Định nghĩa một kiểu đại diện cho các hàm có kiểu
// (string,string) -> (string).
private delegate string MyDelegate(string s1, string s2);

2. Ví dụ với delegate

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;
        }
 

    }

}
Ví dụ dưới đây, định nghĩa ra một delegate IntIntToInt đại diện cho các hàm có kiểu hàm là (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
    {
        // Định nghĩa ra một delegate
        // đại diện cho các hàm có kiểu (int, int) -> (int)
        delegate int IntIntToInt(int a, int b);



        public static void Main(string[] args)
        {
            // Tạo một đối tượng delegate. 
            // Truyền (pass) vào tham số là một hàm có cùng kiểu hàm với delegate.
            IntIntToInt iiToInt = new IntIntToInt(MathUtils.sum);

            // Khi bạn thực thi một delegate.
            // Nó sẽ gọi hàm (hoặc phương thức) mà nó đại diện.
            int value = iiToInt(10, 20); // 30

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

            // Gán giá trị khác cho delegate.
            iiToInt = new IntIntToInt(MathUtils.multiple);

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

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

            Console.Read();

        }


    }


}
Chạy ví dụ:
Value = 30
Value = 200
Bạn cũng có thể tạo đối tượng delegate đại điện cho các hàm không tĩnh (none static). Xem ví dụ:
HelloProgramTest.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDelegatesTutorial
{
    class HelloProgramTest
    {
        // Tạo một Delegate
        // Đại diện cho các kiểu hàm: (string) -> (string).
        private delegate string StringStringToString(string s);



        public static void Main(string[] args)  
        {

            // Tạo một đối tượng HelloProgram.
            HelloProgram program = new HelloProgram();

            // Tạo một đối tượng Delegate đại diện cho hàm SayHello.
            StringStringToString ssToS = new StringStringToString(program.SayHello);


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

            Console.WriteLine(greeting);

            Console.Read();
        }

    }


}

3. Hàm trả về một hàm

Trong C#, với Delegate bạn có thể tạo ra một hàm trả về một hàm (Thực tế là một hàm trả về một Delegate).
TaxFormulas.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDelegatesTutorial
{
    class TaxFormulas
    {
        // Một Delegate đại diện cho các hàm kiểu (float) -> (float).
        public delegate float TaxFormula(float salary);

        // Công thức tính thuế của Mỹ (10% lương).
        public static float UsaFormula(float salary)
        {
            return 10 * salary / 100;
        }

        // Công thức tính thuế của Vietnam (5% lương).
        public static float VietnamFormula(float salary)
        {
            return 5 * salary / 100;
        }

        // Công thức tính thuế mặc định (7% lương).
        public static float DefaultFormula(float salary)
        {
            return 7 * salary / 100;
        }

        // Trả về một hàm tính thuế dựa trên mã Quốc gia. (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;

            // Công thức tính thuế theo quốc gia Việt Nam.
            TaxFormulas.TaxFormula formula = TaxFormulas.GetSalaryFormula("VN");

            float tax = formula(salary);

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

            // Công thức tính thuế tại Canada
            formula = TaxFormulas.GetSalaryFormula("CA");

            tax = formula(salary);

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

            Console.Read();
        }
    }

}
Chạy ví dụ:
Tax in Vietname = 50
Tax in Canada = 70

4. Phương thức nặc danh

Dựa vào Delegate bạn có thể tạo ra một phương thức nặc danh (Anonymous method), nó là một phương thức không có tên, chỉ có thân (body) của phương thức, nó là một khối lệnh (block) có 0 hoặc nhiều tham số, và có thể có kiểu trả về.
AnonymousMethod.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDelegatesTutorial
{
    class AnonymousMethod
    {
        // Một Delegate đại diện cho các hàm kiểu: (float) -> (float).
        // Tính thuế dựa trên lương.
        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();
        }
    }

}
Chạy ví dụ:
countryCode = VN, salary = 1000 -> tax = 50

5. Multicasting của một Delegate

C# cho phép bạn cộng (+) hai đối tượng Delegate với nhau để tạo thành một đối tượng Delegate mới. Chú ý rằng các đối tượng Delegate có thể cộng được phải có cùng kiểu hàm, và là hàm không có kiểu trả về. Khi đối tượng delegate mới được gọi, tất cả các delegate con cũng sẽ được thực thi. Khái niệm này được gọi là Multicasting của delegate.
Greetings.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpDelegatesTutorial
{
    class Greetings
    {
        // Kiểu hàm: (String) -> ()
        public static void Hello(String name)
        {
            Console.WriteLine("Hello " + name);
        }

        // Kiểu hàm: (String) -> ()
        public static void Bye(string name)
        {
            Console.WriteLine("Bye " + name);
        }

        // Kiểu hàm: (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
    {
        // Khai báo một Delegate.
        public delegate void Greeting(string name);


        public static void Main(string[] args)
        {
            // Tạo các đối tượng Delegate.
            Greeting hello = new Greeting(Greetings.Hello);
            Greeting bye = new Greeting(Greetings.Bye);
            Greeting hi = new Greeting(Greetings.Hi);
            
            // Tạo một Delegate là hợp của 3 đối tượng trên.
            Greeting greeting = hello + bye;
           
            // Bạn cũng có thể sử dụng toán tử +=
            greeting += hi;

            // Thực thi greeting.
            greeting("Tom");

            Console.Read();


        }
    }

}
Chạy ví dụ:
Hello Tom
Bye Tom
Hi Tom

6. Event là gì

Trong C#, Event là một đối tượng đặc biệt của Delegate, nó là nơi chứa các phương thức, các phương thức này sẽ được thực thi đồng loạt khi sự kiện xẩy ra. Bạn có thể thêm các phương thức sẽ được thực thi vào đối tượng Event của đối tượng phát ra sự kiện.
Example:
Lớp Button mô phỏng một nút, nó định nghĩa ra một Event (sự kiện) để thông báo rằng nó vừa bị nhấn (Click). Khi button bị nhấn, event sẽ được thực thi. Bạn cần thêm các phương thức cần thực thi vào cho đối tượng Event từ bên ngoài.
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);

        // Định nghĩa một sự kiện, nó chưa được gán giá trị.
        // Giá trị của nó được gán từ bên ngoài.
        public event ClickHander OnButtonClick;

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

        // Mô phỏng Button này bị nhấn (Click).
        // Xác định vị trí x, y mà người dùng nhấn vào.
        public void Clicked()
        {
            Random random = new Random();

            // Một số ngẫu nhiên từ 1 -> 100
            int x = random.Next(1, 100);

            // Một số ngẫu nhiên từ 1 -> 20
            int y = random.Next(1, 20);


            if (OnButtonClick != null)
            {
                // Gọi xử lý sự kiện.
                OnButtonClick(this, x, y);
            }
            
        }
    }


}
Lớp MyApplication mô phỏng một ứng dụng có 2 nút, "Open File" và "Save File".

Bạn cần viết phương thức để làm gì đó khi người dùng click vào "Open File", thêm phương thức này vào cho sự kiện OnButtonClick của nút openButton

Bạn cần viết phương thức để làm gì đó khi người dùng click vào "Save File", và thêm phương thức này vào cho sự kiện OnButtonClick của nút 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;

        // Mô phỏng một ứng dụng và có các Button.
        public MyApplication()
        {
            // Thêm 1 Button vào giao diện.
            this.openButton = new Button("Open File");

            // Thêm 1 Button vào giao diện.
            this.saveButton = new Button("Save File");

            // Thêm một phương thức vào sự kiện của button 'Open Button'.
            // (Tính năng Multicasting của Delegate)
            this.openButton.OnButtonClick += this.OpenButtonClicked;

            // Thêm một phương thức vào sự kiện của button 'Save Button'.
            // (Tính năng Multicasting của Delegate)
            this.saveButton.OnButtonClick += this.SaveButtonClicked;
        }

        private void OpenButtonClicked(Button source, int x, int y)
        {
            // Mô phỏng mở ra một cửa sổ để chọn File để mở.
            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)
        {

            // Mô phỏng mở ứng dụng
            MyApplication myApp = new MyApplication();

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

            // Mô phỏng openButton bị click
            myApp.openButton.Clicked();

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

            // Mô phỏng saveButton bị click
            myApp.saveButton.Clicked();


            Console.Read();
            
        }
    }

}
Chạy ví dụ:
User Click on Open Button ....
Open Dialog to Select a file
Openning file: File75_4.txt

User Click on Save Button ....
Saved file: File75_4.txt