委托简介 找?e佫~?
委托是C#中的一种引用类型,类似于C/C++中的函数指针。与函数指针不同的是,委托是面向对象、类型安全的,而且委托可以引用静态方法和实例方法,而函数指针只能引用静态函数。委托主要用于 .NET Framework 中的事件处理程序和回调函数。 w^w僄?抴
一个委托可以看作一个特殊的类,因而它的定义可以像常规类一样放在同样的位置。与其他类一样,委托必须先定义以后,再实例化。与类不同的是,实例化的委托没有与之相应的术语(类的实例化称作对象),作为区分我们将实例化的委托称为委托实例。 8: 粁膑;l
函数指针 $<揩麊?
为什么委托是类型安全的。C#中的委托和指针不一样,指针不通过MSIL而是直接和内存打交道,这也是指针不安全的原因所在,当然也是采用指针能够提高程序运行速度的缘故;委托不与内存打交道,而是把这一工作交给CLR去完成。CLR无法阻止将不安全的代码调用到本机(非托管)代码中或执行恶意操作。然而当代码是类型安全时,CLR的安全性强制机制确保代码不会访问本机代码,除非它有访问本机代码的权限。 诙涿坥( ?
委托派生于基类System.Delegate,不过委托的定义和常规类的定义方法不太一样。委托的定义通过关键字delegate来定义: lbS辚/?
b辭?
public delegate int myDelegate(int x,int y); K??1?9?
描Xp墄]?
上面的代码定义了一个新委托,它可以封装任何返回为int,带有两个int类型参数的方法。任何一个方法无论是实例方法还是静态方法,只要他们的签名(参数类型在一个方法中的顺序)和定义的委托是一样的,都可以把他们封装到委托中去。这种签名方法正是保证委托是类型安全的手段之一。 荍 e{蹪?
i榟甑U?
委托的使用: /3i>s遻€
using System;
public class MyClass
{
public static void Main()
{
PrintStr myPrinter = new PrintStr();
PrintHandler myHandler = null;
myHandler += new PrintHandler(myPrinter.CallPrint);
// 将委托链接到方法,来实例化委托
if(myHandler!=null)
myHandler("Hello World!");
// 调用委托,相当于匿名调用委托所链接的方法
Console.Read();
}
}
public delegate void PrintHandler(string str); // 声明委托类型
public class PrintStr
{
public void CallPrint(string input)
{
Console.WriteLine(input);
}
}
在C#中使用委托方法: 嵇?:z鲞)
创建委托所使用的方法必须和委托声明相一致(参数列表、返回值都一致) '耔?;&~
利用 +=、-=来进行委托的链接、取消链接或直接使用Delegate.Combine和Delegate.Remove方法来实现 )瘌FF鮒
可以使用MulticastDelegate的实例方法GetInvocationList()来获取委托链中所有的委托 t譶?謊辕8
不能撰写包含 out 参数的委托 ;I?漏鞗E
Z?qv戚楢?
事件的简介 E?Ml%?
C# 中的“事件”是当对象发生某些事情时,类向该类的客户提供通知的一种方法。 €P?骅,W溠Q
1、事件的声明: ,箐?8?2
声明的格式为:<access modifier> event <delegate type> EventName 習羚?
因为使用委托来声明事件,所以在类里声明事件时,首先必须先声明该事件的委托类型<delegate type>(如果尚未声明的话)。在上面我们已经提到过了委托类型的声明,但是在.net framework下为事件使用的委托类型进行声明时有更严格的规定: 緳ju骐>应
(1)、 事件的委托类型应采用两个参数; ?瞒Y詋y3
(2)、两个参数分别是:指示事件源的“对象源”参数和封装事件的其他任何相关信息的“e”参数; .凤p竪颻埝
(3)、“e”参数的类型应为EventArgs 类或派生自 EventArgs 类。 Q軳HB=?
叫>W坐/~
如下的定义: >G[p髓坬
public delegate void PrintHandler(object sender,System.EventArgs e); Bh:B?擫a
??(?俆D
然后我们才能声明该委托类型的事件 n皦1g?Q
例如: HP$豓嫲堋?
public event PrintHandler Print; *俑s舰播?
当事件发生时,将调用其客户提供给它的委托。 祴i唃裌#?
餐鶃疗o燩
2、调用事件: ?E瑸芺綒?
类声明了事件以后,可以就像处理所指示的委托类型的字段那样处理该事件。如果没有任何客户将委托与该事件绑定,则该字段将为空;否则该字段引用应在调用该事件时调用的委托。因此,调用事件时通常先检查是否为空,然后再调用事件。(调用事件,即触发事件,只能从声明该事件的类内进行) 選H嫙?
:Z岤憱髄|?
if(Print != null) 噅戔氋??
{ ??2放e|N
Print (this,e); 郍笛2R?g
} H$?訁偟Bc
鹟 ?奲 ?
3、事件绑定: 仪"泷?啣s
从类的外面来看,事件就象类的一个公共成员,通过 类名.事件名 的形式来访问,但是只能对它做绑定和解除绑定的操作,而不能有其他操作。 諡T秘趺?
e蘕唿钄 ?
类名. Print += new PrintHandler(绑定的方法名) 罛6媌>1圿
// 将某个方法绑定到Print事件上 訥瞢刮鈗?
类名. Print -= new PrintHandler(绑定的方法名) 騻,8?剓
// 将某个已绑定到Print事件上的方法从Print事件上解除 幚
using System;
public class MyClass
{
private static bool check = true;
public static void Main()
{
PrintStr print = new PrintStr();
print.Print += new PrintHandler(print_Print);
while(MyClass.check)
{
print.AddNum();
}
System.Console.WriteLine("事件被激活,退出循环!");
}
private static void print_Print(string str)
{
MyClass.check = false;
}
}
public delegate void PrintHandler(string str); // 声明委托类型
public class PrintStr
{
public event PrintHandler Print;
private int a = 0;
public PrintStr()
{
Print = new PrintHandler(this.CallPrint);
}
public void CallPrint(string input)
{
System.Console.WriteLine(input);
}
public void AddNum()
{
a ++;
if(a >= 10)
{
Print("a已经等于10了。");
}
}
}
这段代码的作用就是,在不停的执行print.AddNum()时当a被累加到10的时候会激活一个事件,让这个循环终止运行。
using System;
namespace Events
{
public delegate void NumberReachedEventHandler(object sender, NumberReachedEventArgs e);
public class Counter
{
public event NumberReachedEventHandler NumberReached;
public Counter()
{
}
public void CountTo(int reachableNum)
{
NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum);
OnNumberReached(e);
return;
}
protected virtual void OnNumberReached(NumberReachedEventArgs e)
//可以被重载
{
if(NumberReached!=null)
{
NumberReached(this, e);
}
}
}
public class NumberReachedEventArgs : EventArgs
{
private int _reached;
public NumberReachedEventArgs(int num)
{
this._reached = num;
}
public int ReachedNumber
{
get
{
return _reached;
}
}
}
}