委托,是我委托你處理事件


什么是委托呢,MSDN給出這樣的定義:

委托是安全封裝方法的類型,類似於 C 和 C++ 中的函數指針。與 C 函數指針不同的是,委托是面向對象的、類型安全的和可靠的。委托的類型由委托的名稱確定。以下示例聲明名為 Del 的委托,該委托可以封裝采用字符串作為參數並返回 void 的方法:public delegate void Del(string message);

委托對象通常通過提供委托將封裝的方法的名稱或使用匿名方法構造。對委托進行實例化后,委托會將對其進行的方法調用傳遞到該方法。調用方傳遞到委托的參數將傳遞到該方法,並且委托會將方法的返回值(如果有)返回到調用方。這被稱為調用委托。實例化的委托可以按封裝的方法本身進行調用。例如:

public static void DelegateMethod(string message)
{
    System.Console.WriteLine(message);
}
View Code

委托類型派生自 .NET Framework 中的 Delegate 類。委托類型是封裝的,它們不能派生出其他類,也不能從 Delegate 派生出自定義類。由於實例化的委托是一個對象,因此可以作為參數傳遞或分配給一個屬性。這允許方法作為參數接受委托並在稍后調用委托。這被稱為異步回調,是在長進程完成時通知調用方的常用方法。當以這種方式使用委托時,使用委托的代碼不需要知道要使用的實現方法。功能類似於封裝接口提供的功能。

回調的另一個常見用途是定義自定義比較方法並將該委托傳遞到短方法。它允許調用方的代碼成為排序算法的一部分。以下示例方法使用 Del 類型作為參數:

 
public void MethodWithCallback(int param1, int param2, Del callback)
{
    callback("The number is: " + (param1 + param2).ToString());
}
View Code

然后,你可以將上面創建的委托傳遞到該方法:

MethodWithCallback(1, 2, handler);

並將以下輸出接收到控制台:

The number is: 3

以抽象方式使用委托時,MethodWithCallback 不需要直接調用控制台,記住,其不必設計為具有控制台。 MethodWithCallback 的作用是簡單准備字符串並將字符串傳遞到其他方法。由於委托的方法可以使用任意數量的參數,此功能特別強大。

當委托構造為封裝實例方法時,委托將同時引用實例和方法。委托不知道除其所封裝方法以外的實例類型,因此委托可以引用任何類型的對象,只要該對象上有與委托簽名匹配的方法。當委托構造為封裝靜態方法時,委托僅引用方法。請考慮以下聲明:

public class MethodClass
{
    public void Method1(string message) { }
    public void Method2(string message) { }
}
View Code

加上之前顯示的靜態 DelegateMethod,我們現在已有三個 Del 實例可以封裝的方法。

調用時,委托可以調用多個方法。這被稱為多播。若要向委托的方法列表(調用列表)添加其他方法,只需使用加法運算符或加法賦值運算符(“+”或“+=”)添加兩個委托。例如:

MethodClass obj = new MethodClass();
Del d1 = obj.Method1;
Del d2 = obj.Method2;
Del d3 = DelegateMethod;

//Both types of assignment are valid.
Del allMethodsDelegate = d1 + d2;
allMethodsDelegate += d3;
View Code

此時,allMethodsDelegate 的調用列表中包含三個方法,分別為 Method1、Method2 和 DelegateMethod。原有的三個委托(d1、d2 和 d3)保持不變。調用 allMethodsDelegate 時,將按順序調用所有三個方法。如果委托使用引用參數,引用將按相反的順序傳遞到所有這三個方法,並且一種方法進行的任何更改都將在另一種方法上見到。當方法引發未在方法內捕獲到的異常時,該異常將傳遞到委托的調用方,並且不會調用調用列表中的后續方法。如果委托具有返回值和/或輸出參數,它將返回上次調用方法的返回值和參數。若要刪除調用列表中的方法,請使用減法運算符或減法賦值運算符(“+”或“+=”)。例如:

//remove Method1
allMethodsDelegate -= d1;

// copy AllMethodsDelegate while removing d2
Del oneMethodDelegate = allMethodsDelegate - d2;
View Code

由於委托類型派生自 System.Delegate,因此可以在委托上調用該類定義的方法和屬性。例如,若要查詢委托調用列表中方法的數量,你可以編寫:

int invocationCount = d1.GetInvocationList().GetLength(0);

調用列表中具有多個方法的委托派生自 MulticastDelegate,該類屬於 System.Delegate 的子類。由於這兩個類都支持 GetInvocationList,因此在其他情況下,上述代碼也將產生作用。

多播委托廣泛用於事件處理中。事件源對象將事件通知發送到已注冊接收該事件的接收方對象。若要注冊一個事件,接收方需要創建用於處理該事件的方法,然后為該方法創建委托並將委托傳遞到事件源。事件發生時,源調用委托。然后,委托將對接收方調用事件處理方法,從而提供事件數據。給定事件的委托類型由事件源確定。有關詳細信息,請參閱事件(C# 編程指南)。

在編譯時比較分配的兩個不同類型的委托將導致編譯錯誤。如果委托實例是靜態的 System.Delegate 類型,則允許比較,但在運行時將返回 false。例如:

delegate void Delegate1();
delegate void Delegate2();

static void method(Delegate1 d, Delegate2 e, System.Delegate f)
{
    // Compile-time error.
    //Console.WriteLine(d == e);

    // OK at compile-time. False if the run-time type of f 
    // is not the same as that of d.
    System.Console.WriteLine(d == f);
}
 
View Code

在 C# 1.0 及更高版本中,可以按以下示例所示聲明委托。

  // 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);
View Code

C# 2.0 提供了更簡單的方法來編寫上面的聲明,如以下示例所示。

Del del2 = Notify;

在 C# 2.0 及更高版本中,還可以使用匿名方法來聲明和初始化委托,如以下示例所示。

Del del3 = delegate(string name) { Console.WriteLine("Notification received for: {0}", name); };

在 C# 3.0 及更高版本中,還可以使用 Lambda 表達式來聲明和實例化委托,如以下示例所示。

Del del4 = name =>  { Console.WriteLine("Notification received for: {0}", name); };

有關更多信息,請參見 Lambda 表達式(C# 編程指南)。

下面的示例闡釋聲明、實例化和使用委托。 BookDB 類封裝一個書店數據庫,它維護一個書籍數據庫。它公開 ProcessPaperbackBooks 方法,該方法在數據庫中查找所有平裝書,並對每本平裝書調用一個委托。使用的 delegate 類型名為 ProcessBookDelegate。 Test 類使用該類打印平裝書的書名和平均價格。

委托的使用促進了書店數據庫和客戶代碼之間功能的良好分隔。客戶代碼不知道書籍的存儲方式和書店代碼查找平裝書的方式。書店代碼也不知道找到平裝書后將對平裝書執行什么處理。

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);
            }
        }
    }
}
View Code

 

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

    // Class 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
*/
View Code

 

  • 聲明委托。

    下面的語句聲明一個新的委托類型。

    public delegate void ProcessBookDelegate(Book book);
    

    每個委托類型都描述參數的數目和類型,以及它可以封裝的方法的返回值類型。每當需要一組新的參數類型或新的返回值類型時,都必須聲明一個新的委托類型。

  • 實例化委托。

    聲明了委托類型后,必須創建委托對象並使之與特定方法關聯。在上一個示例中,您通過按下面示例中的方式將 PrintTitle 方法傳遞到ProcessPaperbackBooks 方法來實現這一點:

    bookDB.ProcessPaperbackBooks(PrintTitle);
    

    這將創建與靜態方法 Test.PrintTitle 關聯的新委托對象。類似地,對象 totaller 的非靜態方法 AddBookToTotal 是按下面示例中的方式傳遞的:

    bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal);
    

    在兩個示例中,都向 ProcessPaperbackBooks 方法傳遞了一個新的委托對象。

    委托創建后,它的關聯方法就不能更改;委托對象是不可變的。

  • 調用委托。

    創建委托對象后,通常將委托對象傳遞給將調用該委托的其他代碼。通過委托對象的名稱(后面跟着要傳遞給委托的參數,括在括號內)調用委托對象。下面是委托調用的示例:

    processBook(b);
    

    與本例一樣,可以通過使用 BeginInvoke 和 EndInvoke 方法同步或異步調用委托。

  • 如何:合並委托(多路廣播委托)

  • 本示例演示如何創建多播委托。  委托對象的一個有用屬性是:可以使用 + 運算符將多個對象分配給一個委托實例。多播委托包含已分配委托的列表。在調用多播委托時,它會按順序調用列表中的委托。只能合並相同類型的委托。

    - 運算符可用於從多播委托中移除組件委托。

    using System;
    
    // Define a custom delegate that has a string parameter and returns void.
    delegate void CustomDel(string s);
    
    class TestClass
    {
        // Define two methods that have the same signature as CustomDel.
        static void Hello(string s)
        {
            System.Console.WriteLine("  Hello, {0}!", s);
        }
    
        static void Goodbye(string s)
        {
            System.Console.WriteLine("  Goodbye, {0}!", s);
        }
    
        static void Main()
        {
            // Declare instances of the custom delegate.
            CustomDel hiDel, byeDel, multiDel, multiMinusHiDel;
    
            // In this example, you can omit the custom delegate if you 
            // want to and use Action<string> instead.
            //Action<string> hiDel, byeDel, multiDel, multiMinusHiDel;
    
            // Create the delegate object hiDel that references the
            // method Hello.
            hiDel = Hello;
    
            // Create the delegate object byeDel that references the
            // method Goodbye.
            byeDel = Goodbye;
    
            // The two delegates, hiDel and byeDel, are combined to 
            // form multiDel. 
            multiDel = hiDel + byeDel;
    
            // Remove hiDel from the multicast delegate, leaving byeDel,
            // which calls only the method Goodbye.
            multiMinusHiDel = multiDel - hiDel;
    
            Console.WriteLine("Invoking delegate hiDel:");
            hiDel("A");
            Console.WriteLine("Invoking delegate byeDel:");
            byeDel("B");
            Console.WriteLine("Invoking delegate multiDel:");
            multiDel("C");
            Console.WriteLine("Invoking delegate multiMinusHiDel:");
            multiMinusHiDel("D");
        }
    }
    /* Output:
    Invoking delegate hiDel:
      Hello, A!
    Invoking delegate byeDel:
      Goodbye, B!
    Invoking delegate multiDel:
      Hello, C!
      Goodbye, C!
    Invoking delegate multiMinusHiDel:
      Goodbye, D!
    */
    View Code

     

    QQ群:108845298,期待你的加入


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM