委托和事件
回调(call back)函数是windows编程的一个重要部分,回调函数实际上就是方法调用的指针,也称为函数指针,是一个非常强大的编程特性。在.NET中以委托的形式实现了函数指针的概念,委托是类型安全的。本文主要描述C#中委托和事件的原理和实现。
一、委托
1.1 在C#中使用委托
在C#中,最好将委托看作是对象的一种新类型。使用委托和类一样,需要先定义,然后实例化。定义委托的语法如下:
委托的实例化语法如下:
注意:给定委托的实例时,可以表示任何类型的任何对象上的实例方法或静态方法——只要方法的特征匹配于委托的特征即可。
1.2 委托实例
下面给出两个使用委托的实例
结果为:
1.3 多播委托
前面介绍的委托都只包含一个方法调用,实际上委托可以包含多个方法,包含多个方法的委托叫做多播委托。如果调用多播委托,就可以按照顺序连续调用多个方法。为此多播委托的返回值必须是void(否则,返回值送到何处?)实际上,如果编译器发现某个委托返回void,就会自动假定这是一个多播委托。
二、事件
在开发基于对象的应用程序时,对象之间需要通信,例如在一个对象中发生了什么有趣的事情时,需要通知其它对象发生了什么变化,这里就需要用到事件。可以把事件作为对象之间通信的介质。而委托就用作应用程序接收到消息时封装事件的方式。
下面通过一个例子来说明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();//委托实例的调用
}
{
//.
}
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 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
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
//类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;
注意:如果使用多播委托,就应注意对同一个委托调用方法链的顺序并未正式定义,因此应避免编写依赖于任意特定顺序调用方法的代码。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; }
}
}
}
//用于生成事件的窗口包含一个按钮和一个标签
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; }
}
}
}