与委托有关的语法:
定义委托:<modifiers> delegate <return_type> <delegate_name> (<argument_list>)
public delegate void Message() ;
创建委托实例:<delegate_type> <name> = new <delegate_type> (<method>)
Message msg = new Message(Messages.Greeting);
调用委托:<delegate_name> (<argument_list>)
msg();
多播委托是指 引用多个方法的委托。当调用委托时,它连续调用每个方法。为了把委托的单个实例合并为一个 多播委托, 委托必须是同类型的, 返回类型必须是void,不能带输出参数选择(但可以带引用参数)。多播委托 用于C#的事件模型中。
示例:多播委托
using
System;
public delegate void Message(); // 定义一个无参数的委托
public class Messages //Messages类定义了三个方法打印不同的消息。返回类型为void,不带参数
{
public static void Greeting()
{
Console.WriteLine("Welcome to Mandolin Co.");
}
public static void DateAndTime()
{
Console.WriteLine(DateTime.Now.ToLongDateString());
}
public static void Maintenance()
{
Console.WriteLine("System maintenance will be done tonight");
}
}
public delegate void Message(); // 定义一个无参数的委托
public class Messages //Messages类定义了三个方法打印不同的消息。返回类型为void,不带参数
{
public static void Greeting()
{
Console.WriteLine("Welcome to Mandolin Co.");
}
public static void DateAndTime()
{
Console.WriteLine(DateTime.Now.ToLongDateString());
}
public static void Maintenance()
{
Console.WriteLine("System maintenance will be done tonight");
}
}
using
System;
class MultiDemo
{
public static void Main()
{
Message msg;
//Create a multi-cast delegate that will print out a number of messages
msg = new Message(Messages.Greeting); //创建委托实例
msg += new Message(Messages.DateAndTime);
Message msg2 = new Message(Messages.Maintenance); //创建委托实例
msg += msg2;//三个Message委托串在一起形成了多播委托
msg(); //调用委托
//A delegate is removed from the multi-cast
Console.WriteLine();
msg -= msg2;//从多播委托msg中删除了一个委托msg2
msg(); //调用委托
Console.ReadKey();
}
}
class MultiDemo
{
public static void Main()
{
Message msg;
//Create a multi-cast delegate that will print out a number of messages
msg = new Message(Messages.Greeting); //创建委托实例
msg += new Message(Messages.DateAndTime);
Message msg2 = new Message(Messages.Maintenance); //创建委托实例
msg += msg2;//三个Message委托串在一起形成了多播委托
msg(); //调用委托
//A delegate is removed from the multi-cast
Console.WriteLine();
msg -= msg2;//从多播委托msg中删除了一个委托msg2
msg(); //调用委托
Console.ReadKey();
}
}
运行结果:
Welcome to Mandolin Co.
2008年3月1日
System maintenance will be done tonight
Welcome to Mandolin Co.
2008年3月1日
2. 事 件
2.1 C#事件模型
C#使用一种委托模型来实现事件。 事件处理方法不必在将生成事件的类中定义。设想应用程序中有两个按钮,但这两个按钮的作用不同。如果 事件处理程序被绑定到 事件源,我们可能必须写两个派生的按钮类,每个派生类有自己的事件处理程序。而 在委托模型下,按钮类仍是通用的,只是事件处理程序必须分别定义。 事件处理程序可以(通常)放在不同的类中。
在委托模型下需要的是 把事件源和事件处理程序连接起来的一种机制。这 是委托发挥作用的地方。委托提供对指定了返回类型和参数列表的方法的一般引用。方法做什么对委托并不重要。 事件委托 可以定义为生成事件的类的一个成员。将用来处理事件的方法和事件处理委托关联起来。当 事件发生时,调用委托,然后调用事件处理方法。
事件处理委托是多播的。
事件的基本生命周期过程为:事件生成者把事件委托的一个实例定义为它的成员。事件消费者是那些希望在事件发生时得到通知的对象。它们定义将和事件委托关联的事件处理方法。当生成事件时,事件生成者通过调用事件委托“触发”事件。然后委托调用和它关联的事件处理方法。
事件委托把两个对象发送给每个事件处理方法。第一个是对生成事件的对象的引用,称为事件源。第二个对象将是System.EventArgs类的一个实例,或者EventArgs的一个派生类的实例。该对象包含了关于事件的额外信息。
2.2 事件委托
事件委托的一般形式:
<modifiers> delegate void <delegate_name>(object source, EventArgs e);
其参数列表中总是有两个参数。第一个参数代表事件源。第二个参数是EventArgs类的一个实例,或者它的派生类的一个实例,它包含事件的另外的信息。例如:MouseEventArgs类定义下列属性:
public MouseButtons Button { get; }
public int Clicks { get; }
public int Delta { get; }
public int X { get;}
public int Y { get;}
这些属性告诉事件处理程序哪个按钮引发了事件,事件中有几次单击,和事件发生的地方。
.NET Framework类库中的事件委托
这些内建的委托都派自System.Delegate类。下面是System.Windows.Forms命名空间中包含的几个事件处理委托:
public delegate void ColumnClickEventHandler(object source, ColumnClickEventArgs args)
public delegate void DragEventHandler(object source, DragEventArgs args)
public delegate void KeyEventHandler(object source, KeyEventArgs args)
public delegate void MouseEventHandler(object source, MouseEventArgs args)
可知,所有这些 内建的委托都遵循标准的事件委托格式:返回类型为void,参数列表中有两个参数。
用户定义的事件委托
您也可以按照事件委托的一般形式定义自己的事件委托。也可以定义自己的从EventArgs类派生的类,包含和委托有关的信息。例如:如果想定义一个委托响应某个对象名称的变化,可以这样定义:
public delegate void NameEventHandler(object source, NameEventArgs args);
您还 必须定义NameEventArgs类。
创建事件委托实例
事件委托实例的创建和 标准委托的创建的不同之处是不使用new关键字,而是使用event关键字。创建上面的委托的实例的语法是:
public event NameEventHandler handler;
event关键字告诉编译器这个委托实例是一个事件。编译器将保证该委托拥有一个事件委托的正确签名。 编译器也将事件委托能进行的操作限制为+=和-=运算符。上面的语句创建一个空引用,指向一个名为handler的事件委托。 空状态表明还没有事件处理程序和这个委托关联起来。
2.3 事件处理程序
事件处理程序是事件生成时事件委托调用的一个方法。事件处理方法可以在一个不同于事件源的类中定义。 因为和事件委托关联在一起,事件处理程序总是和事件委托有相同的参数列表和返回类型。
要把 事件处理程序和事件关联起来,事件必须在它维护的委托的列表中添加和方法相关联的委托。例如:如果想把一个名为NameChange()的事件处理程序和一个名为handler的NameEventHandler实例关联起来,使用的语法为:
handler += new NameEventHandler(NameChange);
2.4 触发事件
要让类能够触发事件,应该把事件委托的一个实例定义为类的成员。生成事件的类应该定义确定什么时候生成事件的代码,还应该定义生成提供事件的EventArgs对象的代码。
要触发事件,只需要调用事件委托实例。
3. 用户定义的事件
这个例子中我们创建并应用一个用户定义的事件。NameList类代表在列表中添加一个字符串时生成事件的ArrayList。ArrayList被定义为一个字段。NameList类还把一个NameListEventHandler实例声明为字段。除了构造函数,Add()方法是NameList类包含的唯一的一个函数成员。
Add()方法把指定的字符串添加到ArrayList。它 接着查看是否有事件处理委托已经添加到了NameListEventHandler实例。如果有的话,则调用该事件委托,委托又调用这些方法,把对事件源的引用和NameListEventArgs类的一个实例传递给方法。
NameListEventHandler委托的第二个参数是一个NameListEventArgs对象。 NameListEventArgs类封装了有关NameList类生成的事件的信息。它定义了两个字段,String表示添加到列表中的名称,和一个整型值为列表中名称的当前数量。NameListEventArgs类定义了一个公有的构造函数和两个属性返回字段的值。
using
System;
using System.Collections;
public delegate void NameListEventHandler(object source, NameListEventArgs args);
public class NameList
{
ArrayList list;
public event NameListEventHandler nameListEvent;
public NameList()
{
list = new ArrayList();
}
public void Add(string Name)
{
list.Add(Name);
if (nameListEvent != null)
{
nameListEvent(this, new NameListEventArgs(Name, list.Count));
}
}
}
public class NameListEventArgs : EventArgs
{
string name;
int count;
public NameListEventArgs(string str, int i)
{
name = str;
count = i;
}
public string Name
{
get
{
return name;
}
}
public int Count
{
get
{
return count;
}
}
}
using System.Collections;
public delegate void NameListEventHandler(object source, NameListEventArgs args);
public class NameList
{
ArrayList list;
public event NameListEventHandler nameListEvent;
public NameList()
{
list = new ArrayList();
}
public void Add(string Name)
{
list.Add(Name);
if (nameListEvent != null)
{
nameListEvent(this, new NameListEventArgs(Name, list.Count));
}
}
}
public class NameListEventArgs : EventArgs
{
string name;
int count;
public NameListEventArgs(string str, int i)
{
name = str;
count = i;
}
public string Name
{
get
{
return name;
}
}
public int Count
{
get
{
return count;
}
}
}
EventDemo类创建一个NameList对象。 NameList对象的事件委托在它的列表中添加两个事件处理委托。第一个引用NewName()方法。第二个引用CurrentCount()方法。 这两个方法都在EventDemo类中定义。
然后在NameList中添加两个名称。 每添加一个名称,就触发一个事件,NewName()和CurrentCount()方法都被调用。这些方法输出被添加的名称和列表中名称的个数:
using
System;
public class EventDemo
{
public static void Main()
{
NameList names = new NameList();
names.nameListEvent += new NameListEventHandler(NewName);
names.nameListEvent += new NameListEventHandler(CurrentCount);
names.Add("Flowfield");
names.Add("Bosworth");
Console.ReadKey();
}
public static void NewName(object source, NameListEventArgs args)
{
Console.WriteLine(args.Name + " was added to the list");
}
public static void CurrentCount(object source, NameListEventArgs args)
{
Console.WriteLine("list currently has " + args.Count + " items");
}
}
public class EventDemo
{
public static void Main()
{
NameList names = new NameList();
names.nameListEvent += new NameListEventHandler(NewName);
names.nameListEvent += new NameListEventHandler(CurrentCount);
names.Add("Flowfield");
names.Add("Bosworth");
Console.ReadKey();
}
public static void NewName(object source, NameListEventArgs args)
{
Console.WriteLine(args.Name + " was added to the list");
}
public static void CurrentCount(object source, NameListEventArgs args)
{
Console.WriteLine("list currently has " + args.Count + " items");
}
}
运行结果:
Flowfield was added to the list
list currently has 1 items
Bosworth was added to the list
list currently has 2 items
这个例子一个要注意的要点是, 事件处理代码和事件源是完全分开的。您可以在完全不改变NameList类本身的情况下,改变在NameList中添加一个元素时调用的方法。
摘自<<C# Programmer's Reference>>