委托和事件

委托和事件

回调(call back)函数是windows编程的一个重要部分,回调函数实际上就是方法调用的指针,也称为函数指针,是一个非常强大的编程特性。在.NET中以委托的形式实现了函数指针的概念,委托是类型安全的。本文主要描述C#中委托和事件的原理和实现。

一、委托

1.1 在C#中使用委托

在C#中,最好将委托看作是对象的一种新类型。使用委托和类一样,需要先定义,然后实例化。定义委托的语法如下:

delegate void VoidPreration(uint x);

这里定义了一个委托VoidPreration,并指定该委托的每个实例都包含一个方法的细节,该方法带有一个uint参数,并返回void。委托的类型安全性非常重要,定义委托时,必须给出它所代表的方法的全部细节。
委托的实例化语法如下:

public void SomeMethod(uint x)
{
  //.
}
static void Main()
{  
  VoidPreration voidPreration = new VoidPreration(SomeMethod);//实例化委托
  voidPreration();//委托实例的调用
}


注意:给定委托的实例时,可以表示任何类型的任何对象上的实例方法或静态方法——只要方法的特征匹配于委托的特征即可。

1.2 委托实例

下面给出两个使用委托的实例

//在这个示例中,定义一个类MathsOperation,它有两个静态方法,对double类型的值执行两个操作,然后使用该委托调用这些方法。
using System;
namespace SimepleDelegate
{
  //类 MathsOperations 
  public class MathsOperations
  {
    public static double MultiplyByTwo(double value)
    {
      return value*2;
    }
    public static double Square(double value)
    {
      return value*value;
    }
  }

  //定义委托
  delegate double DoubleOp(double x);
  //测试
  public class MainEntryPoint
  {
    static void Main()
    {
      DoubleOp[] operations = 
          {
             new DoubleOp(MathsOperations.MultiplyByTwo),
             new DoubleOp(MathsOperations.Square)
          }
      private void ProcessAndDisplayNumber(DoubleOp action, double value)
      {
        double result = action(value);
        ConsoleWriteLine("value is {0}, result of operation is {1}",value,result);
      }
      for(int i = 0; i<operations.Length; i++)
      {
        Console.WriteLine("using operation[{0}]:",i);
        ProcessAndDisplayNumber(operation[i],2.0);
        Console.WriteLine();
      }
    }
  }
}


结果为:

using operations[0]:
value is 2,result of operation is 4

using operation[1];
value is 2,result of operation is 4

 

//BubbleSorter示例
//类BubbleSorter执行一个静态方法Sort(),这个方法的第一个参数时一个对象数组,把该数组按照升序重新排列。
//由于排序的数组可以是任意对象,所以这里需要定义一个委托
delegate bool CompareOp(object lhs, object rhs);
//类BubbleSorter
public class BubbleSorter
{
  public static void Sort(object[] sortArray, CompareOp gtMethod)
  {
    for(int i = 0; i<sortArray.Length; i++)
    {
      for(int j = i+1; j<sortArray.Length;j++)
      {
        if(gtMethos(object[j],object[i]))
        {
          object temp = object[i];
          object[i] = object[j];
          object[j] = temp;
        }
      }
    }
  }
}

//定义Employee类,建立要排序的数组
public class Employee
{
  private string name;
  private decimal salary;
  public Employee(string name, decimal salary)
  {
    this.name =  name;
    this.salary = salary;
  }
  public override string ToString()
  {
    return string.Format(name + ", {0,C}",salary);
  }
  //比较大小的方法
  public static bool RhsIsGreater(object lhs, object rhs)
  {
    Employee empLhs = (Employee)lhs;
    Employee empRhs = (Employee)rhs;
    return (empRhs.salary >empLhs.salary)? true:false;
  }
}

//测试
public class MainEntryPoint
{
  Employee[] employees = 
       {
         new Employee("wang",2000),
         new Employee("chen",1000),
         new Employee("zhu",3000),
         new Employee("li",2500)
       };
  CompareOp employeeCompareOp = new CompareOp(Employee.RhsIsGreater);//实例化委托
  BubbleSorter.Sort(employees,employeeCompareOp);//委托调用
  for(int i = 0; i<employees.Length; i++)
  {
    Console.WriteLine(employees[i].ToString());
  }
}

//运行结果

chen, $1,000.00
wang, $2,000.00
li, $2,500.00
zhu, $3,000.00


1.3 多播委托

前面介绍的委托都只包含一个方法调用,实际上委托可以包含多个方法,包含多个方法的委托叫做多播委托。如果调用多播委托,就可以按照顺序连续调用多个方法。为此多播委托的返回值必须是void(否则,返回值送到何处?)实际上,如果编译器发现某个委托返回void,就会自动假定这是一个多播委托。

delegate void DoubleOp(double value);
class MainEntryPoint
{
  static void Main()
  {
    DoubleOp operations = new DoubleOp(MathOperations.MultiplyByTwo);
    operation += new DoubleOp(MathOperations.Square);
    
  }
}
//上面的多播委托等价于下面的代码
DoubleOp operation1 = new DoubleOp(MathOperations.MultiplyByTwo);
DoubleOp operation2 = new DoubleOp(MathOperations.Square);
DoubleOp operations = operation1 + operation2;

注意:如果使用多播委托,就应注意对同一个委托调用方法链的顺序并未正式定义,因此应避免编写依赖于任意特定顺序调用方法的代码。

二、事件

在开发基于对象的应用程序时,对象之间需要通信,例如在一个对象中发生了什么有趣的事情时,需要通知其它对象发生了什么变化,这里就需要用到事件。可以把事件作为对象之间通信的介质。而委托就用作应用程序接收到消息时封装事件的方式。
下面通过一个例子来说明C#中事件的创建、引发、接收和取消事件。
 

//这个例子包含一个窗体,它会引发另一个类正在监听的事件。在引发事件后,接收对象就确定是否执行一个过程,如果该过程未能继续,就取消事件。
//用于生成事件的窗口包含一个按钮和一个标签
namespace EventTest
{
    public  partial class Form1 : Form
    {
        public delegate void ActionEventHandler(object sender, ActionCancelEventArgs ev);
        public static event ActionEventHandler Action;
        public BusEntity busEntity = new BusEntity();
        public Form1()
        {
            InitializeComponent();            
            
        }

        private void OnAction(object sender, ActionCancelEventArgs ev)
        {
            if (Action != null)
            {
                Action(sender, ev);
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ActionCancelEventArgs cancelEvent = new ActionCancelEventArgs();
            OnAction(this, cancelEvent);
            if (cancelEvent.Cancel)
            {
                this.label1.Text = cancelEvent.Message;
            }
            else
            {
                this.label1.Text = this.busEntity.TimeString;
            }
        }

    }
    public class ActionCancelEventArgs : System.ComponentModel.CancelEventArgs
    {
        string msg = "";
        public ActionCancelEventArgs() :base(){}
        public ActionCancelEventArgs(bool cancel) : base(cancel) { }
        public ActionCancelEventArgs(bool cancel, string message)
            : base(cancel)
        {
            this.msg = message;
        }

        public string Message
        {
            get { return this.msg;}
            set {this.msg = value;}
        }
        
    }

    //事件接收器
    public class BusEntity
    {
        string time = "";
        public BusEntity()
        {
            Form1.Action += new Form1.ActionEventHandler(Form1_Action);
        }

        private void Form1_Action(object sender, ActionCancelEventArgs ev)
        {
            ev.Cancel = !DoAction();
            if (ev.Cancel)
            {
                ev.Message = "Wasn't the right time";
            }
        }
        private bool DoAction()
        {
            bool retVal = false;
            DateTime tm = DateTime.Now;
            if (tm.Second < 30)
            {
                this.time = "The time is " + DateTime.Now.ToLongTimeString();
                retVal = true;
            }
            else
            {
                this.time = "";                
            }
            return retVal;
        }
        public string TimeString
        {
            get { return this.time; }
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值