c# 详解委托

首先我们来全面认识一下委托

  委托是一种安全地封装方法的类型,委托是面向对象的、类型安全的和保险的。

  委托的类型由委托的名称定义。

  下面的示例声明了一个名为 Del 的委托,该委托可以封装一个采用字符串作为参数并返回 void 的方法。

     public delegate void Del(string message);

构造委托对象时,通常提供委托将包装的方法的名称或使用匿名方法

实例化委托后,委托将把对它进行的方法调用传递给方法。调用方传递给委托的参数被传递给方法,来自方法的返回值(如果有)由委托返回给调用方。这被称为调用委托。

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

 

Del handler = DelegateMethod;

handler("Hello World");

委托类型派生自 Delegate 类。委托类型是密封的,不能从 Delegate 中派生委托类型,也不可能从中派生自定义类。

由于实例化委托是一个对象,所以可以将其作为参数进行传递,也可以将其赋值给属性。这样,方法便可以将一个委托作为参数来接受,并且以后可以调用该委托。这称为异步回调,是在较长的进程完成后用来通知调用方的常用方法。以这种方式使用委托时,使用委托的代码无需了解有关所用方法的实现方面的任何信息。此功能类似于接口所提供的封装。

回调的另一个常见用法是定义自定义的比较方法并将该委托传递给排序方法。它允许调用方的代码成为排序算法的一部分。下面的示例方法使用 Del 类型作为参数:

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

然后可以将上面创建的委托传递给该方法:

MethodWithCallback(1, 2, handler);

在控制台中将收到下面的输出:

The number is: 3

将委托构造为包装实例方法时,该委托将同时引用实例和方法。除了它所包装的方法外,委托不了解实例类型,所以只要任意类型的对象中具有与委托签名相匹配的方法,委托就可以引用该对象。将委托构造为包装静态方法时,它只引用方法。考虑下列声明:

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

加上前面显示的静态 DelegateMethod,现在我们有三个方法可由 Del 实例进行包装。

调用委托时,它可以调用多个方法。这称为多路广播。若要向委托的方法列表(调用列表)中添加额外的方法,只需使用加法运算符或加法赋值运算符(“+”或“+=”)添加两个委托。例如:

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

Del allMethodsDelegate = d1 + d2;
allMethodsDelegate += d3;

此时,allMethodsDelegate 在其调用列表中包含三个方法 -- Method1Method2DelegateMethod。原来的三个委托 d1d2d3 保持不变。调用 allMethodsDelegate 时,将按顺序调用所有这三个方法。如果委托使用引用参数,则引用将依次传递给三个方法中的每个方法,由一个方法引起的更改对下一个方法是可见的。如果任一方法引发了异常,而在该方法内未捕获该异常,则该异常将传递给委托的调用方,并且不再对调用列表中后面的方法进行调用。如果委托具有返回值和/或输出参数,它将返回最后调用的方法的返回值和参数。若要从调用列表中移除方法,请使用减法运算符或减法赋值运算符(“-”或“-=”)。例如:

allMethodsDelegate -= d1;

Del oneMethodDelegate = allMethodsDelegate - d2;

由于委托类型派生自 System.Delegate,所以可在委托上调用该类定义的方法和属性。例如,为了找出委托的调用列表中的方法数,您可以编写下面的代码:

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

在调用列表中具有多个方法的委托派生自 MulticastDelegate,这是 System.Delegate 的子类。由于两个类都支持 GetInvocationList,所以上面的代码在两种情况下都适用。

多路广播委托广泛用于事件处理中。事件源对象向已注册接收该事件的接收方对象发送事件通知。为了为事件注册,接收方创建了旨在处理事件的方法,然后为该方法创建委托并将该委托传递给事件源。事件发生时,源将调用委托。然后,委托调用接收方的事件处理方法并传送事件数据。给定事件的委托类型由事件源定义。

在编译时,对分配了两种不同类型的委托进行比较将产生编译错误。如果委托实例静态地属于类型 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);
}

以上讲解的是委托的基本应用要去上班。先写到这里。

接着上面

下面我们来具体分析带命名方法的委托和带匿名方法的委托

委托可以于命名方法关联,使用命名方法对委托进行实例化时,该方法将作为参数传递,

例如:

deleteGate void del(int x);

void  DoWork(int k){/*-------*/}

del = obj.DoWork;

这被称为使用命名的方法。 使用命名方法构造的委托可以封装静态方法或实例方法。

 

作为委托参数传递的方法必须与委托声明具有相同的签名。

委托实例可以封装静态或实例方法。

尽管委托可以使用 out 参数,但建议您不要将其用于多路广播事件委托,因为您无法知道哪个委托将被调用。

以下是声明及使用委托的一个简单示例。

首先我们来创建一个委托

delegate void Del(int i, double j)

class MathClass

{

  static void Main()

  {

    MathClass m = new MathClass();

    Del d = m.MultiplyNumbers;

    System.Console.WriteLine("实例化委托用'MultiplyNumbers':");

      for (int i = 1; i <= 5; i++)
          {
              d(i, 2);
          }
     System.Console.WriteLine("Press any key to exit.");
          System.Console.ReadKey();

   //创建于委托关联的方法

   void MultiplyNumbers(int m, double n)
      {
          System.Console.Write(m * n + " ");
      }

    

  }

}

 

在下面的示例中,一个委托被同时映射到静态方法和实例方法,并分别返回特定的信息。

// Declare a delegate
delegate void Del();

class SampleClass
{
    public void InstanceMethod()
    {
        System.Console.WriteLine("一个消息来自实例化方法.");
    }

    static public void StaticMethod()
    {
        System.Console.WriteLine("一个消息来自静态方法.");
    }
}

class TestSampleClass
{
    static void Main()
    {
        SampleClass sc = new SampleClass();

  Del d = sc.InstanceMethod;

  d();

        d = SampleClass.StaticMethod;
        d();
    }
}

 还有一种方式就是多路广播,主要是应用+ 运算符号 和- 运算符号把多个委托进行串联在一起,执行顺序也是按照当串联的顺序执行。

同时也可以使用- 去掉已经关联好的委托。

下面这个实例讲述啦委托的基本用法

可以好好体会一下这里代码的写法为什么要这样写

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

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

 

posted on 2012-07-16 07:25  361741352  阅读(1074)  评论(1编辑  收藏  举报

导航