Delegate và Event trong Csharp

Khái niệm delegate được hiểu như là một con trỏ hàm (method). Thực tế là một method nó cũng có 1 vùng nhớ xác định trên Ram và khi biết được địa chỉ của vùng nhớ này thì mình có thể gọi method thực hiện, bình thường mình gọi method thông qua đối tượng (instance) hoặc tên class (nếu là method static) và tên method. Như vậy một delegate được hiểu nôm na là một kiểu dùng để lưu trữ 1 hay nhiều địa chỉ method mà những method này phải cùng prototype (gồm kiểu trả về và danh sách các tham số). Khi ta có một biến delegate đang nắm giữ danh sách các method (có cùng prototype với nó) thì chỉ cần khởi động biến này tất cả các danh sách method đó sẽ được thực hiện lần lượt mà ta không cần quan tâm đến tên method đó nữa.

Delegate rất có ý nghĩa khi kết hợp với event, như bác sangcn đã đề cập thì event là một thể hiện đặc biệt của delegate. Theo khái niệm thì event là một thông điệp được gửi tới những đối tượng đã đăng ký trước đó để thực hiện hành động nào đó kèm theo khi thông điệp đó được gửi tới (nghĩa là sự kiện đó xảy ra). Ví dụ, khi chúng ta click chuột vào 1 nút (button) nào đó trên một form (cửa sổ ứng dụng), nghĩa là lúc này đối tượng nút sẽ truyền một thông điệp (phát ra sự kiện nhấn chuột) cho đối tượng form để gọi tới method mà form này đã đăng ký nhận thông điệp khi sự kiện nhấn chuột xảy ra.

Một ứng dụng cụ thể để hiểu mục đích sử dụng delegate event đó là chúng ta viết một class clock (đồng hồ), và một yêu cầu rằng khi người ta sử dụng đối tượng clock này thì muốn nhận một thông điệp (sự kiện) EndTime (hết giờ) để thực hiện một hành động gì đó kèm theo sự kiện này. Ví dụ ta viết một chương trình game đoán chữ và có giới hạn thời gian cho người chơi là 10 giây, hết thời gian này thì chương trình sẽ thông báo "Time over" chẳng hạn. Như vậy, ta sẽ cần ít nhất 2 class là Game và Clock. Chúng ta hãy xem mã nguồn để thể hiện ví dụ này.

public class Clock
{
    private int _second;
    // Định nghĩa một delegate để trỏ vào những method có kiểu không đối số, không kiểu trả về
    public delegate void TimeOver();
    // Khai báo 1 event thuộc kiểu delegate trên
    public event TimeOver EndTime;
    public Clock(int second)
    {
        _second = second;
    }
    public int Second
    {
        get { return _second; }
        set { _second = value; }
    }
    /// 
    /// Method đếm lùi thời gian cho đến khi _second = 0 thì kết thúc và báo lên sự kiện EndTime
    /// 
    public void Timedown()
    {
        do
        {
            _second--;
            Thread.Sleep(1000);
        }
        while (_second > 0);
        // trước khi gọi event hoặc delegate thì cần kiểm tra biến có null không (nghĩa là có chứa địa chỉ
        // của method nào không) nếu khác null (có chứa 1 ít nhât 1 method) thì mới gọi.
        if (EndTime != null)
            EndTime(); // gọi thực hiện các method đã đăng ký sự kiện.
    }
}
class Game
{
    /// 
    /// Method dùng để thực hiện hành động kèm theo khi sự kiện EndTime (delegate TimeOver) xảy ra
    /// 
    public void GameOver()
    {
        Console.WriteLine("Game over");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Clock c = new Clock(10);
        Game g = new Game();
        // Đăng ký method thực hiện hành động kèm theo khi có sự kiện EndTime xảy ra
        c.EndTime += new Clock.TimeOver(g.GameOver);
        ThreadStart ts = new ThreadStart(c.Timedown);
        Thread ChildThread = new Thread(ts);
        ChildThread.Start();
        Console.WriteLine("Waiting about 10 second...");
    }
}

Chủ đề này là một chủ đề lớn và rất hay vì thế sẽ có video để chúng ta tìm hiểu sâu hơn nữa. Hy vọng bạn cũng phần nào nắm được. Tôi gửi link download mã nguồn cho ví dụ trên để bạn tiện tham khảo.

DemoEvent.rar (23,46 kb)

Nguồn: http://www.hoctinhoctructuyen.com/Questions?page=3&TypeSearch=-1

ABSTRACT CLASS VÀ SEALED CLASS

 

Abstract :

- C# cho phép lớp phương thức hay thuộc tính có thể khai báo abstract. Một lớp abstract không được thể hiện và một phương thức abstract chỉ được đưa ra định nghĩa không được thực thi mà phải được overriden trong bất kỳ lớp kế thừa thừa từ nó. Một phương thức abstract sẽ tự động được khai báo virtual. Nếu một lớp có phương thức abstract thì nó cũng là lớp abstract và được khai báo như sau:

Những lớp được định nghĩa bằng cách dùng từ khóa abstract thì không cho phép khởi tạo đối tượng của lớp ấy.

 

abstract class Shape
{
    public abstract float calculateArea();
    public void displaySomething()        {

        Console.WriteLine("Something is displayed");
    }
}
class Circle : Shape
{
    float radius;
    public override float calculateArea()
    {
        return radius * 22 / 7;
    }
}

 

Sealed:

- C# cho phép các lớp và phương thức được khai báo sealed. Điều này có nghĩa là bạn không được quyền thừa kế lớp đó, nếu là phương thức tức là bạn không được phép override nó.

 

sealed class FinalClass
{
    // etc
}
class DerivedClass : FinalClass //Điều này là sai, trình biên dịch sẽ báo lỗi do kế thừa từ một lớp có khai báo sealed.
{
    // etc
}

class MyClass
{
    public sealed override void FinalMethod()
    {
        // etc.
    }
}
class DerivedClass : MyClass
{
    public override void FinalMethod() // Điều này là sai. trình biên dịch sẽ báo lỗi do ghi đè phương thức có khai báo sealed
    {
    }
}

 

 

SỰ KHÁC BIỆT GIỮA LỚP TRỪU TƯỢNG ABSTRACT VÀ GIAO DIỆN INTERFACE

 

Đối với lập trình C# hay lập trình hướng đối tượng nói chung, vấn đề kế thừa sẽ đem lại nhiều phiền toái nếu bạn chưa nắm rõ về vấn đề này. Vậy thì sự khác biệt giữa lớp abstract interface ở chỗ nào?

Một lớp trừu tượng thì không có thể hiện nghĩa là ta không thể khởi tạo nó bằng toán tử new, và một phương thức trong nó là abstract thì chỉ được đưa ra định nghĩa (khai báo) mà không được thực thi và nó sẽ được overriden lại trong các lớp con kế thừa. Và trong lớp mà tồn tại phương thức abstract thì lớp đó cũng được định nghĩa abstract.

Đối với giao diện thì khác hoàn toàn với lớp trừu tượng, nó định nghĩa một cách cứng nhắc các phương thức và thuộc tính trong chúng nghĩa là không cho phép ta thực thi bất kỳ một đoạn mã nào. Và tất cả các thành viên trong nó đều được định nghĩa công khai (public). Một cách tổng quan về giao diện: Giao diện là ràng buộc, giao ước đảm bảo cho các lớp hay các cấu trúc sẽ thực hiện một điều gì đó. Khi một lớp thực thi một giao diện, thì lớp này báo cho các thành phần client biết rằng lớp này có hỗ trợ các phương thức, thuộc tính, sự kiện và các chỉ mục khai báo trong giao diện.

Một số lưu ý khi sử dụng abstract hay interface

Một class chỉ có thể kế thừa từ một abstract class, nhưng có thể kế thừa nhiều interface.

Trong Interface chỉ có thể khai báo các fields, methods, mà không được hiện thực nó. Còn đối với abstract thì dùng các biến, hiện thực cách methods.

Các fields, methods trong interace đều là public và bắt buộc các class kế thừa phải cài đặt nó (abstract). Trong abstract class thì có các fields, methods có thể là private, internal, public, protected và có thể là abstract hoặc non-abstract.

Interface dùng để gom các hành động cần được hiện thực, các khả năng của một đối tượng, còn abstract class cho các lớp thừa kế cùng 1 loại, tính chất hay trạng thái.

Abstract class có tốc độ thực thi nhanh hơn interface.

Thêm 1 tính năng mới vào interface sẽ phá vỡ toàn bộ các lớp hiện thực, còn abstract thì không.

Ví dụ về interface, các thành viên của interface phải được thực thi trong các lớp mà kế thừa từ nó

 

public interface IPlayer
{
    string Name {get; set;}
    Player Actor { get; set; } 
    Bitmap Image { get; set; }
}

 

Ví dụ dưới đây khai báo một lớp abstract có các thành viên trong nó là abstract và non-abstract, và được thực thi trong lớp con là Faculty

 

abstract class Employee
{
    protected string m_str_department;
    protected double m_db_salary;
    protected int m_i_dateHired;
    public string Department
    {
        get { return m_str_department; }
        set { m_str_department = value; }
    }
    public double Salary
    {
        get { return m_db_salary; }
        set { m_db_salary = value; }
    }
    public int DateHired
    {
        get { return m_i_dateHired; }
        set { m_i_dateHired = value; }
    }
    public override string ToString()
    {
        return "Employee: " + m_str_name
        + "\nEmail: " + m_str_email;
    }
    public abstract double CalculateBonus();
    public abstract int CalculateVacation();
}

class Faculty : Employee
{
    string m_str_rank;
    double m_db_hours;
    public override double CalculateBonus()
    {
        return 1000 + 0.05 * m_db_salary;
    }
    public override int CalculateVacation()
    {
        if (m_i_dateHired > 3)
        {
            if (m_str_rank == "Senior Lecture")
                return 6;
            return 5;
        }
        if (m_str_rank == "Senior Lecture")
            return 5;
        return 4;
    }
}

Nguồn: http://vubka.blogspot.com/2010/08/su-khac-biet-giua-lop-truu-tuong.html

 

 

và một bài viết khác cũng khá dễ hiểu

Interface & Abstract class !

Thường khi mới tiếp xúc với Interface Abstract Class sẽ có nhiều câu hỏi hoàn toàn không ngớ ngẩn rằng "Dùng Interface để làm gì? Trong khi chỉ thừa kế được mỗi cái tên Method!".

Nói kế thừa thì chính xác chỉ có Abstract mới là kế thừa thực sự, Interface là Implement, not Inherit. Trong Abstract, chúng ta xây dựng nên lớp Base với đầy đủ các thành phần của một Class, bao gồm các Property, Method, riêng các Method Abstract thì là No Body ! Cái No Body này giống với Interface, Interface chỉ gồm tên của các Method, nó hoàn toàn No Body, không có gì cả. 

Ví dụ :

interface IGeometry
{
    bool IsEmpty();
    bool Overlaps(Geometry geom);
    Geometry Boundary();
}
abstract class Geometry : IGeometry
{
    protected string _Id;
    public virtual string GetId()
    {
        return _Id;
    }
    public virtual bool Overlaps(Geometry geom)
    {
        return SharpMap.Geometries.SpatialRelations.Overlaps(this, geom);
    }
    public abstract bool IsEmpty();
    public abstract Geometry Boundary();
}

Trong ví dụ trên, IGeometry là một Interface bao gồm 3 prototype – không được định nghĩa thân hàm. Geometry là một Abstract Class  kế thừa luôn  IGeometry Interface. Geometry  có một property kiểu string là _Id, một hàm trả về kiểu string là GetId để trả về giá trị _Id, các thành phần này được thiết kế dùng cho việc kế thừa sau này của các lớp khác. Đồng thời, Geometry bắt buộc chứa 3 hàm đã được mô tả trong IGeometry Interface là Overlap, IsEmpty và Boundary. 

Điều gì làm nên đặc trưng của một Abstract Class, đó chính là 3 hàm abstract không được định nghĩa thân hàm. Các hàm này sẽ được định nghĩa trong các lớp dẫn xuất có  kế thừa  từ Geometry. Lại thêm một câu hỏi nữa, việc định nghĩa các hàm Abstract có bị bắt buộc trong lớp dẫn xuất không, xin thưa rằng bắt buộc! 

Nhìn một cách nào đó, bạn có thể thấy rằng, Interface giống như những câu lạc bộ, và Abstract class là một ông bố trong gia đình. 

Một câu lạc bộ luôn luôn có một bộ quy tắc hay nôi qui mà mọi thành viên luôn phải tuân theo. Một ông bố trong gia đình thì khác, ông có tài sản mà con cháu có thể kế thừa, ông cũng có những qui định trong di chúc – ai là người được kế thừa tài sản của ông! 

Bạn có thể tham gia cùng lúc nhiều câu lạc bộ như việc một Class "kế thừa" (Implement) nhiều Interface. Một khi đã tham gia vào CLB nào, bạn phải tuân thủ tất cả các qui tắc của CLB đó, không quan tâm bạn là thành viên của bao nhiêu CLB. Đó là việc tại sao phải Implement đầy đủ các Interface member.

CLB không để lại tài sản cho các bạn, nhưng bố của bạn thì có, ông ấy cho bạn tài sản (không nếu ông ấy không thích Laughing) và cũng bắt bạn tuân theo các qui tắc – gia qui. Và một khi đã là con của người này thì bạn không thể nào là con của người khác giống như không thể kế thừa từ nhiều Abstract Class!

 Chú ý, đôi khi bạn không thực hiện vài qui định nào đó (có người yêu chẳng hạn), hãy nói với bố bạn, ông ấy sẽ đồng ý! C# cũng thế,lời xin phép là : throw new NotImplementedException();

Nguồn: http://nvhuan.wordpress.com/2010/01/19/interface-abstract-class/

Interface là cái gì vậy??? sài sao???

Interface được sử dụng rất nhiều trong phân tích thiết kế hướng đối tượng,
Khi một đối tượng hiện thực một interface thì nó sẽ phải hiện thực tất cả các thuộc tính và phương thức trong interface đó.
Điều này có ứng dụng là khi 2 đối tượng có quan hệ với nhau, chúng ta chỉ cần làm việc với interface của nó mà không cần biết cụ thể nó là đối tượng nào.
Đoạn code này minh họa điều đã nói: 

public interface IMyInterface
    {
        string Name { get; set; }
        void Print();
    }
    public class MyFace1 : IMyInterface
    {
        public string Name
        {
            get;
            set;
        }

        public void Print()
        {
            Console.WriteLine(Name);
        }
    }
    public class MyFace2 : IMyInterface
    {
        public string Name
        {
            get;
            set;
        }

        public void Print()
        {
            Console.WriteLine(Name);
        }
    }
    public class Implement
    {
        public void Run(IMyInterface face)
        {
            if (null != face)
            {
                face.Print();
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyFace1 f1 = new MyFace1();
            f1.Name = "I'm face1";
            MyFace2 f2 = new MyFace2();
            f2.Name = "I'm face2";
            Implement imp = new Implement();
            imp.Run(f1);
            imp.Run(f2);
            Console.ReadKey();
        }
    }

Các bạn thấy rằng hàm Run trong lớp Implement không cần quan tâm đến đó là face1 hay face2 chỉ cần nó hiện thực IMyInterface là hàm Run có thể làm việc được.
interface còn nhiều ứng dụng, trong vấn đề trả lời cầu hỏi "dùng để làm gì" thì có 1 ví dụ như trên để bạn có thể mường tượng ra nó làm gì.
- Trong các hệ thống nhiều thành phần, mỗi phần được viết bởi nhiều nhóm phát triển khác nhau, interface có tác dụng là một bản yêu cầu để các nhóm khác hiện thực đúng chức năng mà interface yêu cầu, để nhóm khác không cần biết bạn viết gì, chỉ cần quan tâm bạn có những hàm, thuộc tính theo yêu cầu đã đặc tả là họ có thể làm việc được với nhau.
- Cách sử dụng interface cũng muôn vàn, các bạn nên hiểu về nó và khi đó áp dụng vào phân tích thiết kế hướng đối tượng tốt hơn.

 

Chôm từ: http://cione.com.vn/Questions/AnswerQuestion?Id=744&page=2&TypeSearch=-1

Làm thế nào để Declare, Instantiate, và sử dụng là một Delegate

Trong C# 1.0 và trở về sau, các delegate có thể được kê khai như trong ví dụ sau.

// Declare a delegate.
delegate void Del(string str);

// Declare a method with the same signature as the delegate.
static void Notify(string name)
{
          Console.WriteLine("Notification received for: {0}", name);
}

// Create an instance of the delegate.
Del del1 = new Del(Notify);



C# 2.0 cung cấp một cách đơn giản để viết delegate trước đó, như trong ví dụ sau.

// C# 2.0 provides a simpler way to declare an instance of Del.
Del del2 = Notify;

Trong C# 2.0 và sau, nó cũng có thể sử dụng một method anonymous để khai báo và khởi tạo là một delegate, như trong ví dụ sau.

// Instantiate Del by using an anonymous method.
Del del3 = delegate(string name) { Console.WriteLine("Notification received for: {0}", name); };



Trong C# 3.0 và sau, các delegate cũng có thể được khai báo và khởi tạo bằng cách sử dụng là một biểu thức lambda, như trong ví dụ sau.

// Instantiate Del by using a lambda expression.
Del del4 = name => { Console.WriteLine("Notification received for: {0}", name); };



Việc sử dụng các các delegate phát huy tốt chức năng tách giữa các cơ sở dữ liệu mã hiệu sách và các khách hàng. Mã khách hàng không có kiến thức về cách các cuốn sách được lưu trữ hoặc làm thế nào mã hiệu sách tìm thấy cuốn sách bìa mềm. Các mã hiệu sách không có kiến thức về những gì xử lý được thực hiện trên những cuốn sách bìa mềm sau khi nó tìm thấy chúng.

// A set of classes for handling a bookstore:
namespace Bookstore
{
       using System.Collections;

       // Describes a book in the book list:
       public struct Book
       {
               public string Title; // Title of the book.
               public string Author; // Author of the book.
               public decimal Price; // Price of the book.
               public bool Paperback; // Is it paperback?

               public Book(string title, string author, decimal price, bool paperBack)
               {
                      Title = title;
                      Author = author;
                      Price = price;
                      Paperback = paperBack;
               }
        }

        // Declare a delegate type for processing a book:
        public delegate void ProcessBookDelegate(Book book);

        // Maintains a book database.
        public class BookDB
        {
               // List of all books in the database:
               ArrayList list = new ArrayList();

               // Add a book to the database:
               public void AddBook(string title, string author, decimal price, bool paperBack)
               {
                      list.Add(new Book(title, author, price, paperBack));
               }

               // Call a passed-in delegate on each paperback book to process it:
               public void ProcessPaperbackBooks(ProcessBookDelegate processBook)
               {
                      foreach (Book b in list)
                      {
                             if (b.Paperback)
                                   // Calling the delegate:
                                   processBook(b);
                      }
               }
        }
}

// Using the Bookstore classes:
namespace BookTestClient
{
       using Bookstore;

       // Class to total and average prices of books:
       class PriceTotaller
       {
               int countBooks = 0;
               decimal priceBooks = 0.0m;

               internal void AddBookToTotal(Book book)
               {
                       countBooks += 1;
                       priceBooks += book.Price;
               }

               internal decimal AveragePrice()
               {
                       return priceBooks / countBooks;
               }
        }

         // Cla ss to test the book database:
         class TestBookDB
         {
                // Print the title of the book.
                static void PrintTitle(Book b)
                {
                         System.Console.WriteLine(" {0}", b.Title);
                }

                // Execution starts here.
                static void Main()
                {
                           BookDB bookDB = new BookDB();

                           // Initialize the database with some books:
                           AddBooks(bookDB);
                           // Print all the titles of paperbacks:
                           System.Console.WriteLine("Paperback Book Titles:");

                           // Create a new delegate object associated with the static
                           // method Test.PrintTitle:
                           bookDB.ProcessPaperbackBooks(PrintTitle);
                           // Get the average price of a paperback by using
                           // a PriceTotaller object:
                           PriceTotaller totaller = new PriceTotaller();
                           // Create a new delegate object associated with the nonstatic
                           // method AddBookToTotal on the object totaller:
                           bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal);

                           System.Console.WriteLine("Average Paperback Book Price: ${0:#.##}",
                           totaller.AveragePrice());
                  }

                 // Initialize the book database with some test books:
                 static void AddBooks(BookDB bookDB)
                 {
                         bookDB.AddBook("The C Programming Language",
                                  "Brian W. Kernighan and Dennis M. Ritchie", 19.95m, true);
                         bookDB.AddBook("The Unicode Standard 2.0",
                                  "The Unicode Consortium", 39.95m, true);
                         bookDB.AddBook("The MS-DOS Encyclopedia",
                                  "Ray Duncan", 129.95m, false);
                         bookDB.AddBook("Dogbert's Clues for the Clueless",
                                  "Scott Adams", 12.00m, true);
                  }
         }
}


/* Output:
Paperback Book Titles:
The C Programming Language
The Unicode Standard 2.0
Dogbert's Clues for the Clueless
Average Paperback Book Price: $23.97
*/