首页 > 代码库 > Delegate

Delegate

Delegate可以当它是一个占位符,比如你在写代码的时候并不知道你将要处理的是什么。你只需要知道你将要引入的参数类型和输出类型是什么并定义它即可。这就是书本上所传达的方法签名必须相同的意思。

 

与函数指针相比,delegate有许多函数指针不具备的优点。首先,函数指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。

 

委托对象可使用 "+" 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。"-" 运算符可用于从合并的委托中移除组件委托。

使用委托的这个有用的特点,您可以创建一个委托被调用时要调用的方法的调用列表。这被称为委托的 多播(multicasting),也叫组播。

 

调用(invoke)委托,相当于调用委托所绑定的方法,一个委托可以绑定多个方法,使用"+="就可以向委托中添加新的方法,使用"-="可以从委托中删除方法

 

结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:

1.定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。
2.定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。
3.定义事件处理方法,它应当与delegate对象具有相同的参数和返回值类型。
4. 用event关键字定义事件对象,它同时也是一个delegate对象。
5.用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。
6.在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是OnEventName。
7. 在适当的地方调用事件触发方法触发事件。下面是一个简单的例子:

using System;public class EventTest{// 步骤1,定义delegate对象public delegate void MyEventHandler(object sender, System.EventArgs e);// 步骤2省略public class MyEventCls{// 步骤3,定义事件处理方法,它与delegate对象具有相同的参数和返回值类//public void MyEventFunc(object sender, System.EventArgs e){Console.WriteLine("My event is ok!");}}// 步骤4,用event关键字定义事件对象private event MyEventHandler myevent;private MyEventCls myecls;public EventTest(){myecls = new MyEventCls();// 步骤5,用+=操作符将事件添加到队列中this.myevent += new MyEventHandler(myecls.MyEventFunc);}// 步骤6,以调用delegate的方式写事件触发函数protected void OnMyEvent(System.EventArgs e){if(myevent != null)myevent(this, e);}public void RaiseEvent(){EventArgs e = new EventArgs();// 步骤7,触发事件OnMyEvent(e);}public static void Main(){EventTest et = new EventTest();Console.Write("Please input ‘‘a‘‘:");string s = Console.ReadLine();if(s == "a"){et.RaiseEvent();}else{Console.WriteLine("Error");}}}

 

using System;using System.Text;using System.Data;namespace Class1{        //定义事件引发时,需要传的参数    class NewMailEventArgs:EventArgs    {        private readonly string m_from;        private readonly string m_to;        private readonly string m_subject;        public NewMailEventArgs(string from, string to, string subject)        {            m_from = from;            m_to = to;            m_subject = subject;        }        public string From        {            get{return m_from;}        }        public string To        {            get{return m_to;}        }        public string Subject        {            get{return m_subject;}        }    }    //事件所用的委托(链表)    delegate void NewMailEventHandler(object sender, NewMailEventArgs e);    //提供事件的类    class MailManager    {        public event NewMailEventHandler NewMail;        //通知已订阅事件的对象        protected virtual void OnNewMail(NewMailEventArgs e)        {            NewMailEventHandler temp = NewMail; //MulticastDelegate一个委托链表            //通知所有已订阅事件的对象            if(temp != null)                temp(this,e); //通过事件NewMail(一种特殊的委托)逐一回调客户端的方法        }        //提供一个方法,引发事件        public void SimulateNewMail(string from, string to, string subject)        {            NewMailEventArgs e = new NewMailEventArgs(from,to,subject);            OnNewMail(e);        }    }    //使用事件    class Fax    {        public Fax(MailManager mm)        {            //Subscribe             mm.NewMail += new NewMailEventHandler(Fax_NewMail);        }        private void Fax_NewMail(object sender, NewMailEventArgs e)        {            Console.WriteLine("Message arrived at Fax...");            Console.WriteLine("From={0}, To={1}, Subject=‘{2}‘",e.From,e.To,e.Subject);        }        public void Unregister(MailManager mm)        {            mm.NewMail -= new NewMailEventHandler(Fax_NewMail);        }    }    class Print    {        public Print(MailManager mm)        {            //Subscribe ,在mm.NewMail的委托链表中加入Print_NewMail方法            mm.NewMail += new NewMailEventHandler(Print_NewMail);        }        private void Print_NewMail(object sender, NewMailEventArgs e)        {            Console.WriteLine("Message arrived at Print...");            Console.WriteLine("From={0}, To={1}, Subject=‘{2}‘",e.From,e.To,e.Subject);        }        public void Unregister(MailManager mm)        {            mm.NewMail -= new NewMailEventHandler(Print_NewMail);        }    }    class ExcelProgram    {        [STAThread]        static void Main(string[] args)        {                MailManager mm = new MailManager();            if(true)            {                Fax fax = new Fax(mm);                Print prt = new Print(mm);            }            mm.SimulateNewMail("Anco","Jerry","Event test");            Console.ReadLine();        }    }}

 

 

ref和out的区别在C# 中,既可以通过值也可以通过引用传递参数。通过引用传递参数允许函数成员更改参数的值,并保持该更改。若要通过引用传递参数, 可使用ref或out关键字。ref和out这两个关键字都能够提供相似的功效,其作用也很像C中的指针变量。它们的区别是:

1、使用ref型参数时,传入的参数必须先被初始化。对out而言,必须在方法中对其完成初始化。

2、使用ref和out时,在方法的参数和执行方法时,都要加Ref或Out关键字。以满足匹配。

3、out适合用在需要retrun多个返回值的地方,而ref则用在需要被调用的方法修改调用者的引用的时候。

注:在C#中,方法的参数传递有四种类型:传值(by value),传址(by reference),输出参数(by output),数组参数(by array)。传值参数无需额外的修饰符,传址参数需要修饰符ref,输出参数需要修饰符out,数组参数需要修饰符params。传值参数在方法调用过程中如果改变了参数的值,那么传入方法的参数在方法调用完成以后并不因此而改变,而是保留原来传入时的值。传址参数恰恰相反,如果方法调用过程改变了参数的值,那么传入方法的参数在调用完成以后也随之改变。实际上从名称上我们可以清楚地看出两者的含义--传值参数传递的是调用参数的一份拷贝,而传址参数传递的是调用参数的内存地址,该参数在方法内外指向的是同一个存储位置。

方法参数上的 ref 方法参数关键字使方法引用传递到方法的同一个变量。当控制传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。

若要使用 ref 参数,必须将参数作为 ref 参数显式传递到方法。ref 参数的值被传递到 ref 参数。

传递到 ref 参数的参数必须最先初始化。将此方法与 out 参数相比,后者的参数在传递到 out 参数之前不必显式初始化。

属性不是变量,不能作为 ref 参数传递。

如果两种方法的声明仅在它们对 ref 的使用方面不同,则将出现重载。但是,无法定义仅在 ref 和 out 方面不同的重载。

out

方法参数上的 out 方法参数关键字使方法引用传递到方法的同一个变量。当控制传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。

当希望方法返回多个值时,声明 out 方法非常有用。使用 out 参数的方法仍然可以返回一个值。一个方法可以有一个以上的 out 参数。

若要使用 out 参数,必须将参数作为 out 参数显式传递到方法。out 参数的值不会传递到 out 参数。

不必初始化作为 out 参数传递的变量。然而,必须在方法返回之前为 out 参数赋值。

属性不是变量,不能作为 out 参数传递。

Delegate