比较C#的委托与C语言的函数指针,以及用流程图来理解C#中事件,发布与订阅的逻辑

来源:互联网 发布:centos 6.5破解密码 编辑:程序博客网 时间:2024/05/16 08:52

C#的委托与C语言的函数指针的比较

C#的委托与C语言的函数指针功能相同,都是为了把一个方法作为参数传进另一个方法之中。
C#的委托的例子:

using System; using System.Collections.Generic; using System.Text; namespace Delegate {     //定义委托,注册到此委托的方法必须有与委托相同的参数和返回类型。委托是一个类,任何可以声明类的地方都可以声明委托。    public delegate void GreetingDelegate(string name);     class Program     {         private static void EnglishGreeting(string name)         {             Console.WriteLine("Good Morning, " + name);         }         private static void ChineseGreeting(string name)         {             Console.WriteLine("早上好, " + name);         }         //GreetingDelegate 类型的方法作为参数传进下面的方法中        private static void Greeting(string name, GreetingDelegate MakeGreeting)         {             MakeGreeting(name);         }         static void Main(string[] args)         {             Greeting("Jim", EnglishGreeting);             Greeting("吉姆", ChineseGreeting);         }     } } 

以上代码的C语言版本

#include<stdio.h>int main(){    //相当于C#的委托,指向一个拥有返回类型为空,一个参数为字符串的方法    void (*GreetingPointer)(char *);    void EnglishGreeting(char *);    void ChineseGreeting(char *);    //函数指针作为Greeting的参数    void Greeting(char *, void (*GreetingPointer)(char *p));    //函数指针赋值时不用写参数    GreetingPointer=EnglishGreeting;    Greeting("Jim",EnglishGreeting);    GreetingPointer=ChineseGreeting;    Greeting("吉姆",ChineseGreeting);    return 0;}void EnglishGreeting(char *p){    printf("Good Moring,%s\n",p);}void ChineseGreeting(char *p){    printf("Good Moring,%s\n",p);}void Greeting(char *p,void (*GreetingPointer)(char *p2)){    GreetingPointer(p);}

委托的调用和实例化

委托也可以直接调用:

delegate1("Jim");

实例化一个委托时有多种写法:
1,

GreetingDelegate delegate1=new GreetingDelegate(EnglishGreeting);

2,

GreetingDelegate delegate1=EnglishGreeting;

3,匿名方法

GreetingDelegate delegate1=delegate(string name){Console.WriteLine("Good Morning, " + name);}; //调用delegate1("Jim");

4,Lambda表达式

GreetingDelegate delegate1=(string name)=>{Console.WriteLine("Good Morning, " + name);}; 

5,Func 泛型委托

//Func<T> 必须有返回值Func<string,int>delegate1=((name)=>{Console.WriteLine("Good Morning, " + name);return 0;}); 

6,Action泛型委托

Action<string>delegate1=(name)=>{Console.WriteLine("Good Morning, " + name);}; 

多播委托(委托链):
委托可以包含多个方法,这种委托称为多播委托。用‘+=’和‘-=’进行方法的捆绑和解除。

            static void Main(string[] args)         {             GreetingDelegate delegate1;            //delegate1作为对象必须先进行赋值            delegate1=EnglishGreeting;            delegate1+=ChineseGreeting;            Greeting("Jim", delegate1);         } 

输出结果


C#的事件的优势,声明以及调用

委托的缺点:
1,如果声明为private,则无法在外部访问,彻底丧失意义。
2,如果声明为public,实例化之后的委托对象可以被任意赋值,不符合面向对象的封装思想。
3,委托对象注册第一个方法时必须用“=”赋值,之后则用“+=”。

事件相当于一个封装后的委托类型的变量,弥补了上面三个委托的劣势。

1,一个事件,无论如何声明,它都是private的,只能通过调用“事件触发方法”触发事件。
2,无法赋值,只能用“+=”和“-=”进行方法的注册和解除。

事件的声明和调用:

//声明委托public delegate void GreetingDelegate(string name);//声明事件public event GreetingDelegate GreetingEvent;//定义“事件触发方法”public void OnActivateEvent(){//GreetingEvent!=null 的意思为如果有方法注册GreetingEvent事件    if(GreetingEvent!=null){        GreetingEvent("Jim");    }}

约定:事件触发方法一般用“On”+”事件名”命名


C#的委托和事件的意义

面向对象的编程之中,各种不同的类要进行连接与通讯。利用委托与事件可以在各个类之间建立起一种一对一或一对多的监视关系。拥有事件和“触发事件方法”的类可以理解为一个“发布类”,注册,捆绑在事件上的方法称为“事件处理方法”,“事件处理方法”一般不在“发布类”里面而是在其他的类之中,这些拥有“事件处理方法”的类可以理解为“订阅类”。一旦“发布类”发生某种状态改变,它的所有的“订阅类”将会被自动通知并更新—触发事件并调用它捆绑的“事件处理方法”。
发布类不需要知道订阅类的内容,且订阅类可自行决定是否订阅发布类,因此这种监视相当灵活,并有良好的可扩展性。


用流程图来理解C#的事件的逻辑

using System; using System.Collections.Generic; using System.Text; namespace Delegate { //  public delegate void GreetingHandler(string name2);  委托也可在发布类外部声明    //定义发布类    public class Jim{        //订阅类对发布类感兴趣的信息        private string name="Jim";        //声明委托        public delegate void GreetingHandler(string name2);        //声明事件        public event GreetingHandler GreetingEvent;        //定义“事件触发方法”        public void SomeoneSawMe(){            //如果有方法订阅了事件            if(GreetingEvent!=null){                //触发事件,向所有订阅类发布信息,字段'name'                GreetingEvent(name);            }        }    }    //定义订阅类    public class Tom{        //定义“事件处理方法”        public void Greeting(string name){            Console.WriteLine ("Hello,{0}",name);        }    }    public class Lisa{        public void Greeting(string name){            Console.WriteLine ("Hi,{0}",name);        }    }    //客户端    class TheScene{        static void Main(){            //实例化订阅类,发布类            Jim jim=new Jim();            Tom tom=new Tom();            Lisa lisa=new Lisa();            //订阅类的“事件处理方法”注册发布类的事件            jim.GreetingEvent+=tom.Greeting;            jim.GreetingEvent+=lisa.Greeting;            //调用事件触发方法            jim.SomeoneSawMe();        }    }}

这里写图片描述

符合.NET框架准则的事件

MSDN解释与例子

以上的代码根据MSDN的范例进行改写:

using System;using System.Collections.Generic;namespace NET_Framework_Event_Test{    //定义Eventargs类,通过它来储存,传递信息    public class GreetingEventArgs : EventArgs    {        public GreetingEventArgs(string s)        {            name = s;        }        private string name;        public string Name        {            get { return name;}            set { name = value;}        }    }    //定义发布类    public class Jim{        //订阅类对发布类感兴趣的信息        private string myname="Jim";        //声明EventHandler<T>类型的事件,因此无需自定义委托        public event EventHandler<GreetingEventArgs> RaiseGreetingEvent;        //定义触发“事件触发方法”的方法,里面可写一些内容        public void SomeoneSawMe(){            OnRaiseGreetingEvent(new GreetingEventArgs(myname));        }        //定义“事件触发方法”,EventArgs类做形参,定义为virtual,衍生类可重写        protected virtual void OnRaiseGreetingEvent(GreetingEventArgs e){            //做一个事件的临时拷贝,防止多线程模式下最后一个订阅类在下面的handler!=null检测后和事件触发前瞬间解除订阅            EventHandler<GreetingEventArgs> handler=RaiseGreetingEvent;            //如果无订阅类,handler将会为空            if(handler!=null){                //触发事件                handler(this,e);            }        }    }    //定义订阅类    public class Tom{        //传进来发布类的实参        public Tom(Jim jim){            //订阅事件            jim.RaiseGreetingEvent+=Greeting;        }        //定义“事件处理方法”        public void Greeting(object sender,GreetingEventArgs e){            Console.WriteLine ("Hello,{0}",e.Name);        }    }    public class Lisa{        public Lisa(Jim jim){            jim.RaiseGreetingEvent+=Greeting;        }        public void Greeting(object sender,GreetingEventArgs e){            Console.WriteLine ("Hi,{0}",e.Name);        }    }    //客户端    class TheScene{        static void Main(){            //实例化订阅类,发布类            Jim jim=new Jim();            Tom tom=new Tom(jim);            Lisa lisa=new Lisa(jim);            //调用事件触发方法            jim.SomeoneSawMe();        }    }}

符合.Net框架准则的事件的特点
1,事件可以基于 自定义的委托,EventHandler委托,或EventHandler< TEventargs >委托。所有.Net框架类库中的事件都是基于EventHandler委托,因此为符合标准,建议使用EventHandler,或EventHandler< TEventargs >。
2,.NET提供的EventHandler代码
public delegate void EventHandler(object sender, EventArgs e);
事件基于的委托有两个参数,一个为发布类自身,另一个为EventArgs类,或它的派生类
3,在“触发事件方法”上层定义另一个方法容纳触发事件之前要运行的内容。
4,在触发事件方法内,预先拷贝一份事件以防止多线程“竞争条件”的发生。
5,将发布类对象做为实参传入订阅类对象中,并在订阅类对象的构造函数中进行事件订阅

.NET 框架标准事件的流程图

.NET框架标准事件流程

1 0
原创粉丝点击