委托解释

发布时间:2020-03-13 20:47    浏览次数 :

[返回]

C#语言系列讲座
委派

 


经常许多人问的,.Net中的委托以及事件处理。我拿简单的例子说明一下,是现实中的例子:

委派

比如说一个公司(场景),你是老板,手下有两个员工,小张和小王。
你命令小王,如果小张玩游戏,则小王扣去小张500元钱。

委派是C#引入的一种新的数据类型,它非常类似于C/C++中的函数指针,常常用于在编译时未绑定的动态方法调用。与函数指针不同的是委派在C#中完全实现了面向对象,它既可以引用静态方法,也可以引用实例方法,而函数指针只能引用静态方法。C#中的委派同时也是类型安全的。

这就是现实中的委托。

作为一种面向对象的数据类型,委派的使用分为三步:委派声明,委派实例化和委派调用。委派声明就是定义一个封装特定参数类型和返回值类型的方法体(静态方法或实例方法)的数据类型,看下面的示例:

实际上,在写程序中,程序员就是老板,小张和小王就是两个对象。小张玩游戏是一个方法,小张还有一个游戏事件,他玩游戏激发这个事件。而小王就是事件处理对象,他负责把小张的钱扣除500。

delegate int Compute (int left, int right);

所以,委托有如下几个要素:
1 激发事件的对象--就是小张
2 处理对象事件的对象--就是小王
3 定义委托,就是你让小王监视小张。

可以看到,委派类型Compute包含了方法的两个要素: 参数类型和返回值类型。委派类型和方法只有满足下面两个条件,才能说它们是兼容的:

如果这三个要素都满足的话,则你就写出了一个完整事件的处理。

1. 参数的数量相同,并且它们的类型也按顺序相同;

下面有个例子:在vs.net2003 C#控制台应用程序编辑运行成功:
using System;

2. 返回值相同。

namespace CSharpConsole
{
    public class 场景
    {
澳门新葡亰7802网址,        [STAThread]
        public static void Main(string[] args)
        {
    Console.WriteLine("场景开始了.");
    // 生成小王
    小王 w = new 小王();
    // 生成小账
    小张 z = new 小张();

委派类型相同指的是它们声明的类型为同一类型(名字一样)。而委派实例相等则是指它们绑定的方法为同一个方法,或相同的方法按相同的顺序组成的方法链,而它们本身的类型只要兼容即可(同样满足上面两个条件),不必强求同名。

    // 指定监视
    z.PlayGame += new PlayGameHandler(w.扣钱);
           
    // 开始玩游戏
    z.玩游戏();

澳门新葡亰平台游戏,委派实例化就是将委派类型绑定到特定方法的过程,和其他对象的实例化相似,都需要用new语句,只是必须接受和该委派类型兼容的方法名作为new语句的参数。如果是实例方法,必须采用两者中间加点号的方式同时提供实例对象和方法。

    Console.WriteLine("场景结束");
    Console.ReadLine();
         }
    }

委派实例化后就可以像调用方法一样进行委派调用了。下面是一个完整的例子,比较典型地展示了通常使用委派的三个步骤:

    // 负责扣钱的人
    public class 小王
    {
    public 小王()
    {
              Console.WriteLine("生成小王");
    }

using System;

    public void 扣钱(object sender,EventArgs e)
    {
        Console.WriteLine("小王:好小子,上班时间胆敢玩游戏");
澳门新葡亰平台官网,        Console.WriteLine("小王:看看你小子有多少钱");
        小张 f = (小张)sender;
        Console.WriteLine("小张的钱: " + f.钱.ToString());
        Console.WriteLine("开始扣钱");
        System.Threading.Thread.Sleep(500);
        f.钱 = f.钱 - 500;
        Console.WriteLine("扣完了.现在小张还剩下:" + f.钱.ToString());
    }
    }

delegate int Compute(int left, int right);

    // 如果玩游戏,则引发事件
    public class 小张
    {
    // 先定义一个事件,这个事件表示“小张”在玩游戏。
         public event PlayGameHandler PlayGame;
    // 保存小张钱的变量
    private int m_Money;

//委派类型声明

    public 小张()
    {
        Console.WriteLine("生成小张.");
        m_Money = 1000; // 构造函数,初始化小张的钱。
    }

class A

    public int 钱 // 此属性可以操作小张的钱。
    {
        get
        {
            return m_Money;
        }
        set
        {
            m_Money = value;
        }
    }

{

    public void 玩游戏()
    {
        Console.WriteLine("小张开始玩游戏了..");
        Console.WriteLine("小张:CS好玩,哈哈哈!我玩..");
        System.Threading.Thread.Sleep(500);
        System.EventArgs e = new EventArgs();
        OnPlayGame(e);
    }

public int Mul(int num1, int num2)

    protected virtual void OnPlayGame(EventArgs e)
    {
        if(PlayGame != null)
        {
            PlayGame(this,e);
        }
    }
    }

{ return num1*num2;

    // 定义委托处理程序
    public delegate void PlayGameHandler(object sender,System.EventArgs e);

}

}

}


class Test

 

{

2C# 中的委托类似于 C 或 C++ 中的函数指针。使用委托使程序员可以将方法引用封装在委托对象内。然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道将调用哪个方法。与 C 或 C++ 中的函数指针不同,委托是面向对象、类型安全的,并且是安全的。

public static int Add(int num1, int num2)

委托声明定义一种类型,它用一组特定的参数以及返回类型封装方法。对于静态方法,委托对象封装要调用的方法。对于实例方法,委托对象同时封装一个实例和该实例上的一个方法。如果您有一个委托对象和一组适当的参数,则可以用这些参数调用该委托

{ return num1+num2;

委托的一个有趣且有用的属性是,它不知道或不关心自己引用的对象的类。任何对象都可以;只是方法的参数类型和返回类型必须与委托的参数类型和返回类型相匹配。这使得委托完全适合“匿名”调用。

}

此教程包括两个示例:

static void Main()

示例 1 展示如何声明、实例化和调用委托

{ Compute c1=new Compute(Add);

示例 2 展示如何组合两个委托

//静态方法的实例化

此外,还讨论以下主题:

A a=new A();

委托和事件

Compute c2=new Compute(a.Mul);

委托与接口

//实例方法的实例化

示例 1

int r1=c1(2,3);//委派调用

下面的示例阐释声明、实例化和使用委托。BookDB 类封装一个书店数据库,它维护一个书籍数据库。它公开 ProcessPaperbackBooks 方法,该方法在数据库中查找所有平装书,并为每本书调用一个委托。所使用的 delegate 类型称为 ProcessBookDelegate。Test 类使用该类输出平装书的书名和平均价格。

int r2=c2(2,3);//委派调用

委托的使用促进了书店数据库和客户代码之间功能的良好分隔。客户代码不知道书籍的存储方式和书店代码查找平装书的方式。书店代码也不知道找到平装书后将对平装书进行什么处理。

Console.WriteLine(r1);

// bookstore.cs

Console.WriteLine(r2);

using System;

}

 

}

// A set of classes for handling a bookstore:

委派组合

namespace Bookstore

委派组合是指一个委派类型可以同时绑定多个可调用的方法。由于绑定多个方法,C#规定组合委派的返回类型必须为void。当然,这些方法的参数类型也必须都和组合委派的参数类型兼容。委派组合采用“+”或“+=”来将两个委派类型合并为一个新的组合委派类型; 采用“-”或“-=”来从组合委派类型上移除已经绑定一个方法的委派实例或绑定多个方法的组合委派实例。

{

需要注意的是在做委派的组合和移除操作的时候,参与操作的委派类型必须相同——注意是“相同”不是“相等”。看下面的例子:

   using System.Collections;

using System;

   // Describes a book in the book list:

delegate void MyDelegate(string s);

   public struct Book

class Test

   {

{

      public string Title;        // Title of the book.

public static void Hello(string s)

      public string Author;       // Author of the book.

{

      public decimal Price;       // Price of the book.

Console.WriteLine(“Hello, {0}!”, s);

      public bool Paperback;      // Is it paperback?

}

      public Book(string title, string author, decimal price, bool paperBack)

public static void Goodbye(string s)

      {

{

         Title = title;

Console.WriteLine(“Goodbye, {0}!”, s);

         Author = author;

}

         Price = price;

public static void Main()

         Paperback = paperBack;

{

      }

MyDelegate a, b, c, d;

   }

a = new MyDelegate(Hello);

   // Declare a delegate type for processing a book:

b =new MyDelegate(Goodbye);

   public delegate void ProcessBookDelegate(Book book);

c = a + b;//委派的组合

   // Maintains a book database.

d = c - a;//委派的移除

   public class BookDB

a(“A”);

   {

b(“B”);

      // List of all books in the database:

c(“C”);

      ArrayList list = new ArrayList();  

d(“D”);

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

Hello, A!

      }

Goodbye, B!

      // Call a passed-in delegate on each paperback book to process it:

Hello, C!

      public void ProcessPaperbackBooks(ProcessBookDelegate processBook)

Goodbye, C!

      {

Goodbye, D!

         foreach (Book b in list)

可以看到在委派组合后,组合委派类型C同时绑定了两个方法Hello(string s)和Goodbye(string s),从它的输出可以看到这一点。

         {

需要注意的是组合委派中的方法调用是有顺序的,如果在上面的例子中修改“c = a

            if (b.Paperback)

  • b; ”为“c=b + a; ”,将看到输出序列的改变。

            // Calling the delegate:

同样委派的移除也是有顺序的,它总是从绑定的委派实例中的最后开始搜索需要移出的委派实例——注意这里的移除是以委派实例为单位的,而不是以方法为单位。可以将上面的例子进行修改,再看看结果:

               processBook(b);

public static void Main()

         }

{

      }

MyDelegate a, b, c, d;

   }

a = new MyDelegate(Hello);

}

b =new MyDelegate(Goodbye);

// Using the Bookstore classes:

c = a+b+a;//委派的组合

namespace BookTestClient

b+=b;//委派的组合

{

d = c - b;//委派的移除

   using Bookstore;

a(“A”);

   // Class to total and average prices of books:

b(“B”);

   class PriceTotaller

c(“C”);

   {

d(“D”);

      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 Test

   {

      // Print the title of the book.

      static void PrintTitle(Book b)

      {

         Console.WriteLine("   {0}", b.Title);

      }