C中的三层架构委托和事件_第1页
C中的三层架构委托和事件_第2页
C中的三层架构委托和事件_第3页
C中的三层架构委托和事件_第4页
C中的三层架构委托和事件_第5页
已阅读5页,还剩47页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、利用C#语言,调用存储过程,使用三层架构实现2009年3月13日利用C#语言,调用存储过程,使用三层架构实现,我需要实现的功能见问题补充问题为:我需要实现的功能是:调用存储过程,实现将数据库中表的信息,显示到页面中,请帮忙写出DAL层、BLL层、页面显示层,它们的代码?谢谢!答案:先写Dal层public datatable GetTable()SqlConnection conn=new SqlConnection ();conn.ConnectionString=Server=(local);database=db;uid=sa;pwd=sa;/连接数据源conn.Open();SqlCo

2、mmand cmd=new SqlCommand(Select,conn);/其中select为存储过程名称cmd.CommandType = CommandType.StoredProcedure;/指定执行类型为存储过程DataTable dt = new DataTable();/执行存储过程SqlDataAdapter sda=new SqlDataAdapter(cmd);/将结果填充到datatable中sda.Fill(dt);return dt;/返回DatatableBll层/实例化Dal层方法DAL dal=new DAL();public datatable BllGet

3、Table()/ 获取数据表return dal.GetTable();页面层/实例化Bll层方法Bll bll=new Bll();Datatable dt=new datatable();/ 调用Bll层方法dt=bll.BllGetTable();趣谈三层架构的演变(转有关三层架构有很多理解。下面用养猪为比喻,介绍三层架构究竟是个什么东西。层次结构在现实社会里随处可见。记得有个笑话讲有个村长得意地向他老婆吹牛:“全中国只有四个人比我官大,乡长、县长、省长和国务院总理”。这个笑话也体现了真实社会中分层的现象。社会人群会分层,公司人员结构也会分层,楼房是分层的,甚至做包子的笼屉都是分层的。虽

4、然分层的目的各有不同,但都是为解决某一问题而产生的。所以,分层架构其实是为了解决某一问题而产生的一种解决方案。14.1.1常用的三层架构设计软件系统最常用的一般会讲到三层架构,其实就是将整个业务应用划分为表示层、业务逻辑层、数据访问层等,有的还要细一些,通过分解业务细节,将不同的功能代码分散开来,更利于系统的设计和开发,同时为可能的变更提供了更小的单元,十分有利于系统的维护和扩展。常见的三层架构基本包括如下几个部分,如图 14-1 所示。图 14- 1常见的三层架构数据访问层 DAL :用于实现与数据库的交互和访问,从数据库获取数据或保存数据到数据库的部分。业务逻辑层 BLL :业务逻辑层承上

5、启下,用于对上下交互的数据进行逻辑处理,实现业务目标。表示层 Web :主要实现和用户的交互,接收用户请求或返回用户请求的数据结果的展现,而具体的数据处理则交给业务逻辑层和数据访问层去处理。日常开发的很多情况下为了复用一些共同的东西,会把一些各层都用的东西抽象出来。如我们将数据对象实体和方法分离,以便在多个层中传递, 例如称为 Model 。一些共性的通用辅助类和工具方法,如数据校验、缓存处理、加解密处理等,为了让各个层之间复用,也单独分离出来,作为独立的模块使用,例如称为 Common 。此时,三层架构会演变为如图 14-2 所示的情况。图 14- 2三层架构演变结果业务实体 Model :

6、 用于封装实体类数据结构,一般用于映射数据库的数据表或视图,用以描述业务中客观存在的对象。 Model 分离出来是为了更好地解耦,为了更好地发挥分层的作用,更好地进行复用和扩展,增强灵活性。通用类库 Common :通用的辅助工具类。在第 5.2 节中我们讲过可以将对数据库的共性操作抽象封装成数据操作类(例如 DbHelperSQL ),以便更好地复用和使代码简洁。数据层底层使用通用数据库操作类来访问数据库,最后完整的三层架构如图 14-3 所示。图 14- 3最后完整的三层架构数据库访问类 是对 ADO.NET 的封装,封装了一些常用的重复的数据库操作。如微软的企业库 SQLHelper.c

7、s ,动软的 DBUtility/DbHelperSQL 等,为 DAL 提供访问数据库的辅助工具类。通过以上分析,我们知道如今常用的三层架构是个什么样子,同时,我们也知道了三层架构在使用过程中的一些演化过程。那么,为什么要这样分层,每层结构到底又起什么作用呢?我们继续往下看。14.1.2趣味理解:三层架构与养猪看新闻报道今年猪肉价格一路高涨,据说有人养猪都发财致富奔小康了,程序员都说写代码没前途了,还不如去养猪,不过,可别认为养猪没有技术含量,比写代码容易,其实养猪也大有学问。为了更好地理解三层架构,就拿养猪来做个例子吧。俗话说:“没吃过猪肉,还没见过猪跑啊!”。图 14-4 是三层架构化的

8、养猪产业流水线趣味对此图。图 14-4三层结构与养猪对比图 14-3 与图 14- 4 ,我们可以看出:数据库好比猪圈 ,所有的猪有序地按区域或编号,存放在不同的猪栏里。DAL 好比是屠宰场 ,把猪从猪圈取出来进行(处理)屠杀,按要求取出相应的部位(字段),或者进行归类整理(统计),形成整箱的猪肉(数据集),传送给食品加工厂( BLL )。本来这里都是同一伙人既管抓猪,又管杀猪的,后来觉得效率太低了,就让一部分人出来专管抓猪了( DBUtility ),根据要求来抓取指定的猪。BLL 好比食品加工厂 ,将猪肉深加工成各种可以食用的食品(业务处理)。Web 好比商场 ,将食品包装成漂亮的可以销售

9、的产品,展现给顾客( UI 表现层)。猪肉好比 Model ,无论是哪个厂(层),各个环节传递的本质都是猪肉,猪肉贯穿整个过程。通用类库 Common 相当于工人使用的各种工具,为各个厂(层)提供诸如杀猪刀、绳子、剪刀、包装箱、工具车等共用的常用工具(类)。其实,每个部门本来是可以自己制作自己的工具的,但是那样会使效率比较低,而且也不专业,并且很多工作都会是重复的。因此,就专门有人开了这样的工厂来制作这些工具,提供给各个工厂,有了这样的分工,工厂就可以专心做自己的事情了。当然,这里只是形象的比喻,目的是为了让大家更好地理解,实际的情况在细节上会有所不同。这个例子也只是说明了从猪圈到商场的单向过

10、程,而实际三层开发中的数据交互是双向的,可取可存。不过,据说有一种机器,把猪从这头赶进去,另一头就噗噗噜噜地出火腿肠了。如果火腿肠卖不了了,从那头再放进去,这头猪又原原本本出来了,科幻的机器吧,没想到也可以和三层结构联系上。以上只是笑谈,不过也使三层架构的基本概念更容易理解了。上面谈了那么多,有人会问,我直接从数据库取出内容直接操作不可以吗?为什么要这么麻烦地用三层架构呢?三层架构到底有什么好处呢?不分层,当然可以,就好比整个过程不分屠宰场、加工场之类的,都在同一个场所(工厂)完成所有的活(屠杀、加工、销售)。但为什么要加工厂和商场呢?因为当规模比较大的时候,管理起来就会变得非常复杂,这样的养

11、殖方式已经无法满足规模化的需要了。并且,从社会的发展来看,社会分工是人类进步的表现 。社会分工的优势就是让适合的人做自己擅长的事情,使平均社会劳动时间大大缩短,生产效率显著提高。能够提供优质高效劳动产品的人才能在市场竞争中获得高利润和高价值。人尽其才,物尽其用最深刻的含义就是由社会分工得出的。软件开发也一样,做小项目的时候,分不分层确实看不出什么差别,并且显得更麻烦啰嗦了。但当项目变大和变复杂时,分层就显示出它的优势来了。所以分不分层要根据项目的实际情况而定,不能一概而论。三层架构(2010-04-22 09:35:19) HYPERLINK javascript:; 转载标签: HYPERL

12、INK /c.php?t=blog&k=%C8%FD%B2%E3%BC%DC%B9%B9&ts=bpost&stype=tag t _blank 三层架构 HYPERLINK /c.php?t=blog&k=it&ts=bpost&stype=tag t _blank it分类: HYPERLINK /s/articlelist_1717044161_1_1.html t _blank C#三层架构(3-tier application) 通常意义上的三层架构就是将整个业务应用划分为:表现层(UI)、业务逻辑层(BLL)、数据访问层(DAL)。区分层次的目的即为了“高内聚,低耦合”的思想。、表

13、现层(UI):通俗讲就是展现给用户的界面,即用户在使用一个系统的时候他的所见所得。、业务逻辑层(BLL):针对具体问题的操作,也可以说是对数据层的操作,对数据业务逻辑处理。、数据访问层(DAL):该层所做事务直接操作数据库,针对数据的增添、删除、修改、更新、查找等。在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。微软推荐的分层式结构一般分为三层,从下至上分别为:数据访问层、业务逻辑层(又或成为领域层)、表示层。三层结构原理:3个层次中,系统主要功能和业务逻辑都在业务逻辑层进行处理。所谓三层体系结构,是在客户端与数据库之间加入了一个“中间层”,也叫组件层。这里所说的三层体系,不是

14、指物理上的三层,不是简单地放置三台机器就是三层体系结构,也不仅仅有B/S应用才是三层体系结构,三层是指逻辑上的三层,即使这三个层放置到一台机器上。三层体系的应用程序将业务规则、数据访问、合法性校验等工作放到了中间层进行处理。通常情况下,客户端不直接与数据库进行交互,而是通过COM/DCOM通讯与中间层建立连接,再经由中间层与数据库进行交互。表示层:位于最外层(最上层),离用户最近。用于显示数据和接收用户输入的数据,为用户提供一种交互式操作的界面。业务逻辑层:业务逻辑层(Business Logic Layer)无疑是系统架构中体现核心价值的部分。它的关注点主要集中在业务规则的制定、业务流程的实

15、现等与业务需求有关的系统设计,也即是说它是与系统所应对的领域(Domain)逻辑有关,很多时候,也将业务逻辑层称为领域层。例如Martin Fowler在Patterns of Enterprise Application Architecture一书中,将整个架构分为三个主要的层:表示层、领域层和数据源层。作为领域驱动设计的先驱Eric Evans,对业务逻辑层作了更细致地划分,细分为应用层与领域层,通过分层进一步将领域逻辑与领域逻辑的解决方案分离。业务逻辑层在体系架构中的位置很关键,它处于数据访问层与表示层中间,起到了数据交换中承上启下的作用。由于层是一种弱耦合结构,层与层之间的依赖是向下

16、的,底层对于上层而言是“无知”的,改变上层的设计对于其调用的底层而言没有任何影响。如果在分层设计时,遵循了面向接口设计的思想,那么这种向下的依赖也应该是一种弱依赖关系。因而在不改变接口定义的前提下,理想的分层式架构,应该是一个支持可抽取、可替换的“抽屉”式架构。正因为如此,业务逻辑层的设计对于一个支持可扩展的架构尤为关键,因为它扮演了两个不同的角色。对于数据访问层而言,它是调用者;对于表示层而言,它却是被调用者。依赖与被依赖的关系都纠结在业务逻辑层上,如何实现依赖关系的解耦,则是除了实现业务逻辑之外留给设计师的任务。数据层:数据访问层:有时候也称为是持久层,其功能主要是负责数据库的访问,可以访

17、问数据库系统、二进制文件、文本文档或是XML文档。简单的说法就是实现对数据表的Select,Insert,Update,Delete的操作。如果要加入ORM的元素,那么就会包括对象和数据表之间的mapping,以及对象实体的持久化。优点:1、开发人员可以只关注整个结构中的其中某一层;2、可以很容易的用新的实现来替换原有层次的实现;3、可以降低层与层之间的依赖;4、有利于标准化;5、利于各层逻辑的复用。缺点:1、降低了系统的性能。这是不言而喻的。如果不采用分层式结构,很多业务可以直接造访数据库,以此获取相应的数据,如今却必须通过中间层来完成。2、有时会导致级联的修改。这种修改尤其体现在自上而下的

18、方向。如果在表示层中需要增加一个功能,为保证其设计符合分层式结构,可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码。规则:三层结构的程序不是说把项目分成DAL, BLL, WebUI三个模块就叫三层了, 下面几个问题在你的项目面:1. UILayer里面只有少量(或者没有)的SQL语句或者存储过程调用, 并且这些语句保证不会修改数据?2. 如果把UILayer拿掉, 你的项目还能在Interface/API的层次上提供所有功能吗?3. 你的DAL可以移植到其他类似环境的项目吗?4. 三个模块, 可以分别运行于不同的服务器吗?如果不是所有答案都为YES, 那么你的项目还不能算是严格意义上

19、的三层程序. 三层程序有一些需要约定遵守的规则:1. 最关键的, UI层只能作为一个外壳, 不能包含任何BizLogic的处理过程.2. 设计时应该从BLL出发, 而不是UI出发. BLL层在API上应该实现所有BizLogic, 以面向对象的方式.3. 不管数据层是一个简单的SqlHelper也好, 还是带有Mapping过的Classes也好, 应该在一定的抽象程度上做到系统无关.4. 不管使用COM+(Enterprise Service), 还是Remoting, 还是WebService之类的远程对象技术, 不管部署的时候是不是真的分别部署到不同的服务器上, 最起码在设计的时候要做这

20、样的考虑, 更远的, 还得考虑多台服务器通过负载均衡作集群.所以考虑一个项目是不是应该应用三层/多层设计时, 先得考虑下是不是真的需要? 实际上大部分程序就开个WebApplication就足够了, 完全没必要作的这么复杂. 而多层结构, 是用于解决真正复杂的项目需求的。C#委托和事件例析 (2010-04-08 14:18:51) HYPERLINK javascript:; 转载标签: HYPERLINK /c.php?t=blog&k=it&ts=bpost&stype=tag t _blank it分类: HYPERLINK /s/articlelist_1717044161_1_1.

21、html t _blank C#ah_bill是对Java了解相对较多,而对C#则是因工作需要才去看了一下,C#跟Java在语法上非常相似,而最初让我比较困惑的就是委托、事件部分,相信大多数初学者也有类似的困惑。经过跟Java的对比学习,发现这其实跟Java的监听、事件是等同的,只是表述上不同罢了。委托+事件是观察者模式的一个典型例子,所谓的委托其实就是观察者,它会关心某种事件,一旦这种事件被触发,这个观察者就会行动。下面是最近写的一个例子,相信能够加深大家对委托和事件的理解。using System;using System.Collections.Generic;using System.

22、Text;namespace ConsoleApplication3public delegate void TimeEventHandler(object obj, TimeEventArgsargs); /定义一个委托,委托其实就是“方法模板”,就好像“类”是“对象”的模板一样。如果某个类想在事件触发的时候收到通知,它必须有一个符合这种格式的方法,在这个例子中,就是:返回类型为void,参数类型为object、TimeEventArgs。/TimeEventArgs是我们自己定义的一个类,用于保存事件中的参数。这里我们分别保存时间的时分秒。public class TimeEventArg

23、s:EventArgs private int hour;private int minute;private int second;public TimeEventArgs(int hour, int minute, int second)this.hour = hour;this.minute = minute;this.second = second;public int Hourgetreturn hour;public int Minutegetreturn minute;public int Secondgetreturn second;/这是一个观察者类,它有一个符合我们上面定义

24、的“委托”的方法,也就是void ShowTime(object obj, TimeEventArgs args),从这个方法的定义可以看到,我们只会关心返回类型和方法的参数,而方法名称则无所谓。class MyTimeEventHandlerClasspublic void ShowTime(object obj, TimeEventArgs args)Console.WriteLine(现在的时间是:+args.Hour+:+args.Minute+:+args.Second);/时钟类class Clock/我们在这个类中定义了一个“TimeChanged”事件,注意其前面有两个关键字“

25、event”和“TimeEventHandler”,其中event表示这是一个事件,而不是方法或属性;TimeEventHandler则指出,谁要监听TimeChanged事件,它就必须有一个符合TimeEventHandler(委托)的方法。public event TimeEventHandler TimeChanged;public Clock()TimeChanged = null; /注意,这里的null的含义是指TimeChanged事件当前还没有观察者关注它,如果某个观察者要关注TimeChanged事件,它必须要让这个事件知道,方法是使用操作符“+=”来借助委托将其加载到事件上

26、。/时钟开始走动,我们的目标是每秒钟触发一次TimeChanged事件public void go()DateTime initi = DateTime.Now;int h1 = initi.Hour;int m1 = initi.Minute;int s1 = initi.Second;while (true)DateTime now = DateTime.Now;int h2 = now.Hour;int m2 = now.Minute;int s2 = now.Second;if (s2!=s1)h1 = h2;m1 = m2;s1 = s2;/首先建立一个TimeEventArgs对象

27、来保存相关参数,这里是时分秒。TimeEventArgs args = new TimeEventArgs(h2,m2, s2);/注意这种写法,这一句是用来触发事件,事件不是类,所以不用使用“new”关键字,而且我们看到,这里TimeChanged的两个参数跟我们的委托(TimeEventHandler)是一致的,其中第一个参数是触发这个事件的对象,我们这里使用的是一个时钟实例(this)。TimeChanged(this, args);class Programstatic void Main(string args)Clock clock = new Clock(); /实例化一个时钟M

28、yTimeEventHandlerClass tehc = new MyTimeEventHandlerClass(); /实例化一个观察者类/将事件跟我们定义的观察者进行连接,这样,clock就会知道,每当TimeChanged事件被触发,就会去通知这个观察者,注意我们连接的时候使用的并不是直接的观察者类实例中的ShowTime()方法,而是一个委托,并在这个委托中传递ShowTime()方法,这也是“委托”的真正意义所在我有一个方法,但我委托你来帮我关联到事件,因为事件只会直接跟委托打交道,而不是观察者的具体某个方法。clock.TimeChanged+=new TimeEventHand

29、ler(tehc.ShowTime);clock.go();C#中的委托和事件B(精) (2010-04-08 11:24:29) HYPERLINK javascript:; 转载标签: HYPERLINK /c.php?t=blog&k=it&ts=bpost&stype=tag t _blank it分类: HYPERLINK /s/articlelist_1717044161_1_1.html t _blank C#事件和委托的编译代码这时候,我们注释掉编译错误的行,然后重新进行编译,再借助Reflactor来对 event的声明语句做一探究,看看为什么会发生这样的错误:publice

30、ventGreetingDelegateMakeGreet;可以看到,实际上尽管我们在GreetingManager里将 MakeGreet 声明为public,但是,实际上MakeGreet会被编译成私有字段,难怪会发生上面的编译错误了,因为它根本就不允许在GreetingManager类的外面以赋值的方式访问,从而验证了我们上面所做的推论。我们再进一步看下MakeGreet所产生的代码:privateGreetingDelegateMakeGreet;/对事件的声明实际是声明一个私有的委托变量MethodImpl(MethodImplOptions.Synchronized)publicv

31、oidadd_MakeGreet(GreetingDelegate value)this.MakeGreet = (GreetingDelegate) Delegate.Combine(this.MakeGreet, value);MethodImpl(MethodImplOptions.Synchronized)publicvoidremove_MakeGreet(GreetingDelegate value)this.MakeGreet = (GreetingDelegate) Delegate.Remove(this.MakeGreet, value);现在已经很明确了:MakeGree

32、t事件确实是一个GreetingDelegate类型的委托,只不过不管是不是声明为public,它总是被声明为private。另外,它还有两个方法,分别是add_MakeGreet和remove_MakeGreet,这两个方法分别用于注册委托类型的方法和取消注册。实际上也就是: “+= ”对应 add_MakeGreet,“-=”对应remove_MakeGreet。而这两个方法的访问限制取决于声明事件时的访问限制符。在add_MakeGreet()方法内部,实际上调用了System.Delegate的Combine()静态方法,这个方法用于将当前的变量添加到委托链表中。我们前面提到过两次,说

33、委托实际上是一个类,在我们定义委托的时候:publicdelegatevoidGreetingDelegate(stringname);当编译器遇到这段代码的时候,会生成下面这样一个完整的类:publicsealedclassGreetingDelegate:System.MulticastDelegatepublicGreetingDelegate(objectobject, IntPtr method);publicvirtualIAsyncResult BeginInvoke(stringname, AsyncCallback callback,objectobject);publicv

34、irtualvoidEndInvoke(IAsyncResult result);publicvirtualvoidInvoke(stringname);关于这个类的更深入内容,可以参阅CLR Via C#等相关书籍,这里就不再讨论了。委托、事件与Observer设计模式范例说明上面的例子已不足以再进行下面的讲解了,我们来看一个新的范例,因为之前已经介绍了很多的内容,所以本节的进度会稍微快一些:假设我们有个高档的热水器,我们给它通上电,当水温超过95度的时候:1、扬声器会开始发出语音,告诉你水的温度;2、液晶屏也会改变水温的显示,来提示水已经快烧开了。现在我们需要写个程序来模拟这个烧水的过程,

35、我们将定义一个类来代表热水器,我们管它叫:Heater,它有代表水温的字段,叫做temperature;当然,还有必不可少的给水加热方法BoilWater(),一个发出语音警报的方法MakeAlert(),一个显示水温的方法,ShowMsg()。namespaceDelegate classHeaterprivateinttemperature;/ 水温/ 烧水publicvoidBoilWater() for(inti = 0; i 95) MakeAlert(temperature);ShowMsg(temperature);/ 发出语音警报privatevoidMakeAlert(int

36、param) Console.WriteLine(Alarm:嘀嘀嘀,水已经 0 度了:, param);/ 显示水温privatevoidShowMsg(intparam) Console.WriteLine(Display:水快开了,当前温度:0度。, param);classProgramstaticvoidMain() Heaterht =newHeater();ht.BoilWater();Observer设计模式简介上面的例子显然能完成我们之前描述的工作,但是却并不够好。现在假设热水器由三部分组成:热水器、警报器、显示器,它们来自于不同厂商并进行了组装。那么,应该是热水器仅仅负责烧

37、水,它不能发出警报也不能显示水温;在水烧开时由警报器发出警报、显示器显示提示和水温。这时候,上面的例子就应该变成这个样子:/ 热水器publicclassHeaterprivateinttemperature;/ 烧水privatevoidBoilWater() for(inti = 0; i = 100; i+) temperature = i;/ 警报器publicclassAlarmprivatevoidMakeAlert(intparam) Console.WriteLine(Alarm:嘀嘀嘀,水已经 0 度了:, param);/ 显示器publicclassDisplaypriv

38、atevoidShowMsg(intparam) Console.WriteLine(Display:水已烧开,当前温度:0度。, param);这里就出现了一个问题:如何在水烧开的时候通知报警器和显示器?在继续进行之前,我们先了解一下Observer设计模式,Observer设计模式中主要包括如下两类对象:Subject:监视对象,它往往包含着其他对象所感兴趣的内容。在本范例中,热水器就是一个监视对象,它包含的其他对象所感兴趣的内容,就是temprature字段,当这个字段的值快到100时,会不断把数据发给监视它的对象。Observer:监视者,它监视Subject,当Subject中的某件

39、事发生的时候,会告知Observer,而Observer则会采取相应的行动。在本范例中,Observer有警报器和显示器,它们采取的行动分别是发出警报和显示水温。在本例中,事情发生的顺序应该是这样的:警报器和显示器告诉热水器,它对它的温度比较感兴趣(注册)。热水器知道后保留对警报器和显示器的引用。热水器进行烧水这一动作,当水温超过95度时,通过对警报器和显示器的引用,自动调用警报器的MakeAlert()方法、显示器的ShowMsg()方法。类似这样的例子是很多的,GOF对它进行了抽象,称为Observer设计模式:Observer设计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对

40、象的状态改变时,其他依赖于它的对象会被自动告知并更新。Observer模式是一种松耦合的设计模式。实现范例的Observer设计模式我们之前已经对委托和事件介绍很多了,现在写代码应该很容易了,现在在这里直接给出代码,并在注释中加以说明。usingSystem;usingSystem.Collections.Generic;usingSystem.Text;namespaceDelegate / 热水器publicclassHeaterprivateinttemperature;publicdelegatevoidBoilHandler(intparam);/声明委托publiceventBoi

41、lHandlerBoilEvent;/声明事件/ 烧水publicvoidBoilWater() for(inti = 0; i 95) if(BoilEvent !=null) /如果有对象注册BoilEvent(temperature);/调用所有注册对象的方法/ 警报器publicclassAlarmpublicvoidMakeAlert(intparam) Console.WriteLine(Alarm:嘀嘀嘀,水已经 0 度了:, param);/ 显示器publicclassDisplaypublicstaticvoidShowMsg(intparam) /静态方法Console.

42、WriteLine(Display:水快烧开了,当前温度:0度。, param);classProgramstaticvoidMain() Heaterheater =newHeater();Alarmalarm =newAlarm();heater.BoilEvent += alarm.MakeAlert;/注册方法heater.BoilEvent += (newAlarm().MakeAlert;/给匿名对象注册方法heater.BoilEvent += Display.ShowMsg;/注册静态方法heater.BoilWater();/烧水,会自动调用注册过对象的方法输出为:Alarm

43、:嘀嘀嘀,水已经 96 度了:Alarm:嘀嘀嘀,水已经 96 度了:Display:水快烧开了,当前温度:96度。/ 省略.Net Framework中的委托与事件尽管上面的范例很好地完成了我们想要完成的工作,但是我们不仅疑惑:为什么.Net Framework 中的事件模型和上面的不同?为什么有很多的EventArgs参数?在回答上面的问题之前,我们先搞懂 .Net Framework的编码规范:委托类型的名称都应该以EventHandler结束。委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object 类型,一个 EventArgs类型(或继承自EventArgs)。事

44、件的命名为 委托去掉 EventHandler之后剩余的部分。继承自EventArgs的类型应该以EventArgs结尾。再做一下说明:委托声明原型中的Object类型的参数代表了Subject,也就是监视对象,在本例中是 Heater(热水器)。回调函数(比如Alarm的MakeAlert)可以通过它访问触发事件的对象(Heater)。EventArgs 对象包含了Observer所感兴趣的数据,在本例中是temperature。上面这些其实不仅仅是为了编码规范而已,这样也使得程序有更大的灵活性。比如说,如果我们不光想获得热水器的温度,还想在Observer端(警报器或者显示器)方法中获得它

45、的生产日期、型号、价格,那么委托和方法的声明都会变得很麻烦,而如果我们将热水器的引用传给警报器的方法,就可以在方法中直接访问热水器了。现在我们改写之前的范例,让它符合 .Net Framework 的规范:usingSystem;usingSystem.Collections.Generic;usingSystem.Text;namespaceDelegate / 热水器publicclassHeaterprivateinttemperature;publicstringtype =RealFire 001;/ 添加型号作为演示publicstringarea =China Xian;/ 添加

46、产地作为演示/声明委托publicdelegatevoidBoiledEventHandler(Object sender, BoiledEventArgs e);publiceventBoiledEventHandlerBoiled;/声明事件/ 定义BoiledEventArgs类,传递给Observer所感兴趣的信息publicclassBoiledEventArgs:EventArgspublicreadonlyinttemperature;publicBoiledEventArgs(inttemperature) this.temperature = temperature;/ 可以

47、供继承自 Heater 的类重写,以便继承类拒绝其他对象对它的监视protectedvirtualvoidOnBoiled(BoiledEventArgs e) if(Boiled !=null) / 如果有对象注册Boiled(this, e);/ 调用所有注册对象的方法/ 烧水。publicvoidBoilWater() for(inti = 0; i 95) /建立BoiledEventArgs 对象。BoiledEventArgs e =newBoiledEventArgs(temperature);OnBoiled(e);/ 调用 OnBolied方法/ 警报器publicclass

48、AlarmpublicvoidMakeAlert(Object sender, Heater.BoiledEventArgs e) Heaterheater = (Heater)sender;/这里是不是很熟悉呢?/访问 sender 中的公共字段Console.WriteLine(Alarm:0 - 1: , heater.area, heater.type);Console.WriteLine(Alarm: 嘀嘀嘀,水已经 0 度了:, e.temperature);Console.WriteLine();/ 显示器publicclassDisplaypublicstaticvoidSho

49、wMsg(Object sender, Heater.BoiledEventArgs e) /静态方法Heaterheater = (Heater)sender;Console.WriteLine(Display:0 - 1: , heater.area, heater.type);Console.WriteLine(Display:水快烧开了,当前温度:0度。, e.temperature);Console.WriteLine();classProgramstaticvoidMain() Heaterheater =newHeater();Alarmalarm =newAlarm();hea

50、ter.Boiled += alarm.MakeAlert;/注册方法heater.Boiled += (newAlarm().MakeAlert;/给匿名对象注册方法heater.Boiled +=newHeater.BoiledEventHandler(alarm.MakeAlert);/也可以这么注册heater.Boiled += Display.ShowMsg;/注册静态方法heater.BoilWater();/烧水,会自动调用注册过对象的方法输出为:Alarm:China Xian - RealFire 001:Alarm: 嘀嘀嘀,水已经 96 度了:Alarm:China X

51、ian - RealFire 001:Alarm: 嘀嘀嘀,水已经 96 度了:Alarm:China Xian - RealFire 001:Alarm: 嘀嘀嘀,水已经 96 度了:Display:China Xian - RealFire 001:Display:水快烧开了,当前温度:96度。/ 省略.总结在本文中我首先通过一个GreetingPeople的小程序向大家介绍了委托的概念、委托用来做什么,随后又引出了事件,接着对委托与事件所产生的中间代码做了粗略的讲述。在第二个稍微复杂点的热水器的范例中,我向大家简要介绍了 Observer设计模式,并通过实现这个范例完成了该模式,随后讲述

52、了.Net Framework中委托、事件的实现方式。希望这篇文章能给你带来帮助。C#中的委托和事件A(精)(2010-04-08 11:12:36) HYPERLINK javascript:; 转载标签: HYPERLINK /c.php?t=blog&k=c&ts=bpost&stype=tag t _blank c HYPERLINK /c.php?t=blog&k=%CE%AF%CD%D0&ts=bpost&stype=tag t _blank 委托 HYPERLINK /c.php?t=blog&k=%CA%C2%BC%FE&ts=bpost&stype=tag t _blank

53、事件 HYPERLINK /c.php?t=blog&k=it&ts=bpost&stype=tag t _blank it分类: HYPERLINK /s/articlelist_1717044161_1_1.html t _blank C#C# 中的委托和事件引言委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里别(bi)得慌,混身不自在。本文中,我将通过两个范例由浅入深地讲述什么是委托、为什么要使用委托、事件的由来

54、、.Net Framework中的委托和事件、委托和事件对Observer设计模式的意义,对它们的中间代码也做了讨论。将方法作为方法的参数我们先不管这个标题如何的绕口,也不管委托究竟是个什么东西,来看下面这两个最简单的方法,它们不过是在屏幕上输出一句问候的话语:publicvoidGreetPeople(stringname) / 做某些额外的事情,比如初始化之类,此处略EnglishGreeting(name);publicvoidEnglishGreeting(stringname) Console.WriteLine(Morning, + name);暂且不管这两个方法有没有什么实际意义

55、。GreetPeople用于向某人问好,当我们传递代表某人姓名的name参数,比如说“Jimmy”,进去的时候,在这个方法中,将调用EnglishGreeting方法,再次传递name参数,EnglishGreeting则用于向屏幕输出 “Morning, Jimmy”。现在假设这个程序需要进行全球化,哎呀,不好了,我是中国人,我不明白“Morning”是什么意思,怎么办呢?好吧,我们再加个中文版的问候方法:publicvoidChineseGreeting(stringname)Console.WriteLine(早上好, + name);这时候,GreetPeople也需要改一改了,不然如

56、何判断到底用哪个版本的Greeting问候方法合适呢?在进行这个之前,我们最好再定义一个枚举作为判断的依据:publicenumLanguageEnglish, ChinesepublicvoidGreetPeople(stringname, Language lang)/做某些额外的事情,比如初始化之类,此处略swith(lang)caseLanguage.English:EnglishGreeting(name);break;caseLanguage.Chinese:ChineseGreeting(name);break;OK,尽管这样解决了问题,但我不说大家也很容易想到,这个解决方案的可

57、扩展性很差,如果日后我们需要再添加韩文版、日文版,就不得不反复修改枚举和GreetPeople()方法,以适应新的需求。在考虑新的解决方案之前,我们先看看 GreetPeople的方法签名:publicvoidGreetPeople(stringname, Language lang)我们仅看 string name,在这里,string 是参数类型,name 是参数变量,当我们赋给name字符串“jimmy”时,它就代表“jimmy”这个值;当我们赋给它“张子阳”时,它又代表着“张子阳”这个值。然后,我们可以在方法体内对这个name进行其他操作。哎,这简直是废话么,刚学程序就知道了。如果你再

58、仔细想想,假如GreetPeople()方法可以接受一个参数变量,这个变量可以代表另一个方法,当我们给这个变量赋值 EnglishGreeting的时候,它代表着 EnglsihGreeting() 这个方法;当我们给它赋值ChineseGreeting 的时候,它又代表着ChineseGreeting()方法。我们将这个参数变量命名为 MakeGreeting,那么不是可以如同给name赋值时一样,在调用 GreetPeople()方法的时候,给这个MakeGreeting 参数也赋上值么(ChineseGreeting或者EnglsihGreeting等)?然后,我们在方法体内,也可以像使

59、用别的参数一样使用MakeGreeting。但是,由于MakeGreeting代表着一个方法,它的使用方式应该和它被赋的方法(比如ChineseGreeting)是一样的,比如:MakeGreeting(name);好了,有了思路了,我们现在就来改改GreetPeople()方法,那么它应该是这个样子了:publicvoidGreetPeople(stringname, * MakeGreeting)MakeGreeting(name);注意到 * ,这个位置通常放置的应该是参数的类型,但到目前为止,我们仅仅是想到应该有个可以代表方法的参数,并按这个思路去改写GreetPeople方法,现在就

60、出现了一个大问题:这个代表着方法的MakeGreeting参数应该是什么类型的?NOTE:这里已不再需要枚举了,因为在给MakeGreeting赋值的时候动态地决定使用哪个方法,是ChineseGreeting还是 EnglishGreeting,而在这个两个方法内部,已经对使用“morning”还是“早上好”作了区分。聪明的你应该已经想到了,现在是委托该出场的时候了,但讲述委托之前,我们再看看MakeGreeting参数所能代表的 ChineseGreeting()和EnglishGreeting()方法的签名:publicvoidEnglishGreeting(stringname)pub

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论