第一篇:脑电图(EEG)与事件相关电位(ERP)的区别
脑电图(EEG)与事件相关电位(ERP)有什么区别?
(一)脑电图(EEG)检查:是在头部按一定部位放置8-16个电极,经脑电图机将脑细胞固有的生物电活动放大并连续描记在纸上的图形。正常情况下,脑电图有一定的规律性,当脑部尤其是皮层有病变时,规律性受到破坏,波形即发生变化,对其波形进行分析,可辅助临床对及脑部疾病进行诊断。
脑波按其频率分为:δ波(1-3c/s)θ波(4-7c/s)、α波(8-13c/s)、β波(14-25c/s)γ波(25c/s以上),δ与θ波称为慢波,β与γ波称为快波。依年龄不同其基本波的频率也不同,如3岁以下小儿以δ波为主,3-6岁以θ波为主,随年龄增长,α波逐渐增多,到成年人时以α波为主,但年龄之间无明确的严格界限,如有的儿童4、5岁枕部α波已很明显。正常成年人在清醒、安静、闭眼时,脑波的基本节律是枕部α波为主,其他部位则是以α波间有少量慢波为主。判断脑波是否正常,主要是根据其年龄,对脑波的频率、波幅、两侧的对称性以及慢波的数量、部位、出现方式及有无病理波等进行分析。许多脑部病变可引起脑波的异常。如颅内占位性病变(尤其是皮层部位者)可有限局性慢波;散发性脑炎,绝大部分脑电图呈现弥漫性高波幅慢波;此外如脑血管病、炎症、外伤、代谢性脑病等都有各种不同程度的异常,但脑深部与线部位的病变阳性率很低。须加指出的是,脑电图表现没有特异性,必须结合临床进行综合判断,然而对于癫痫则有决定性的诊断价值,在阗痫发作间歇期,脑电图可有阵发性高幅慢波、棘波、尖波、棘一慢波综合等所谓“痛性放电”表现。为了提高脑电图的阳性率,可依据不同的病变部位采用不同的电极放置方法。如鼻咽电极、鼓膜电极与蝶骨电极,在开颅时也可将电极置于皮层(皮层电极)或埋入脑深部结构(深部电极);此外,还可使用各种诱发试验,如睁闭眼、过度换气、闪光刺激、睡眠诱发、剥夺睡眠诱发以及静脉注射美解眠等。但蝶骨电极与美解眠诱发试验等方法,可给病人带来痛苦与损害,须在有经验者指导下进行。随着科技的日益发展,近年来又有了遥控脑电图与24小时监测脑电图。
(二)脑电地形图(BEAM)
是在EEG的基础上,将脑电信号输入电脑内进行再处理,通过模数转换与付立叶转换,将脑电信号转换为数字信号,处理成为脑电功率谱,按照不同频带进行分类,依功率的多少分级,最终使脑电信号转换成一种能够定量的二维脑波图像,此种图像能客观地反映各部电位变化的空间分布状态,其定量标志可以用数字或颜色表示,再用打印机打印在颅脑模式图上,或贮存在软盘上。它的优越性在于能发现EEG中较难判别的细微异常,提高了阳性率,且病变部位图像直观醒目,定位比较准确,从而客观对大脑机能进行评价。主要应用于缺血性脑血管病的早期诊断及疗效予后的评价,小儿脑发育与脑波变化的研究,视觉功能的研究,大浮肿瘤的定位以及精神药物的研究等。
(三)脑磁图
电流在导体内流动进,导体周围可以产生磁场。同理,脑细胞的电活动也有极微弱的磁场,可用高灵敏度的磁场传感器予以检测,并记录其随时间变化的关系曲线,是即脑磁图,其图形与EEG图形相似。与EEG相比,优点是:可发现有临床意义而又不能被EEG记录到的波形,或检测到皮质局限性的异常电磁活动;此外,磁检器不与头皮接触,也减少了干扰造成的伪差。若与EEG同时描记,还可对不同物理方位的皮质群进行分析。但由于屏蔽、电磁装置以及其他设备复杂、昂贵,目前国内尚无此项设备。
(四)诱发电位
给人体感官、感觉神经或运动皮质、运动神经以刺激,兴奋沿相应的神经通路向中枢或外周传导,在传导过程中,产生的不断组合传递的电位变化,即为诱发电位,对其加以分析,即或反映出不同部位的神经功能状态。由于诱发电位非常微小,须借助电脑对重复刺激的信号进行叠加处理,将其放大,并从淹没于肌电、脑电的背景中提取出来,才能加以描记。主要是对波形、主波的潜伏期、波峰间期与波幅等进行分析,为临床诊断提供参考,目前临床常用的有视觉、脑干听觉、体感、运动与事件相关诱发电位,以及视网膜图与耳蜗电图等,可分别反映视网膜、视觉通路、内耳、听神经、脑干、外周神经、脊髓后索、感觉皮质以及上下运动神经元的各种病变,事件相关诱发电位则用以判断患者的注意力与反应能力。诱发电位具有高度敏感性,对感觉障碍可进行客观评诂,对病变能进行定量判断。对心理精神领域可进行一定的检测,故当前广泛应用于对神经系统病变的早期诊断,病情随访,疗效判断,予后估计,神经系统发育情况的评估以及协助判断昏迷性质与脑死亡等。但图形无特异性,必须结合临床资料进行判断;不在有关神经传导径路中的病变,不能发现异常。近年,诱发电位的频谱分析与诱发电位地形图也在临床上逐渐开始应用,进一步提高了其临床应用价值。
(五)肌电图(EMG)
是用肌电图仪记录神经与肌肉的生物电活动,对其波形进行测量分析,可以了解神经、肌肉的功能状态,协助对下运动神经元或肌肉疾病的诊断。目前常用的方法有三种:①针极肌电图:亦称普通肌电图,是将特制的针电极刺入肌腹,或用表面电极置于肌肉表面皮肤,在示波器上或记录纸上观察肌肉在静止、轻收缩、重收缩三种状态下的电位变化,以帮助判断疾病究系神经源性或肌源性损害。②神经传导速度测定:也即运动神经传导速度(MCV)与感觉神经传导速度(SCV)测定。系在神经干的近端(MCV)或远端(SCV)给以脉冲刺激,在远端效应肌(MCV)或近端神经走行部位(SCV)接收波形,测理两点之间的潜伏期与距离,即可计算出运动神经或感觉神经传导速度,主要用于了解神经传导功能情况。③其他:如重复频率试验,F波、H反射、牵张反射等检查以及单纤维肌电图检查等,可进一步了解神经、肌肉、神经一肌接头以及脊髓反射弧的功能状态。
(六)脑阻抗血流图(REG)
是检查头部血管功能与供血情况的一种方法。其原理是通过放置在头部的电极给以微弱的高频电流,由于血液的电阻率最小,其电阻可随心动周期供血的变化而变化,这种节律性的阻抗变化,经血流图仪放大,可描记出波动性曲线,对其进行测量、计算、分析,可间接了解外周阻力、血管弹性与供血情况。本法简便易行,但因影响因素比较多,如情绪、气温、检查当时的血管功能状态等,故对其判断应加慎重。须结合临床症状,体征等进行判断。常用于脑动脉硬化、闭塞性脑血管病、偏头痛以及药物疗效观察等。
第二大项.ERP
(一)事件相关电位的基本概念
对大脑高级心理活动如认知过程作出客观评价,我们很难将意识或思维单纯归于大脑某一部位组织、细胞或神经递质的改变,因为仅采用具体、微观的自然科学手段如神经分子生物学、神经生化学难以解决具体的心理活动。二十世纪六十年代,Sutton提出了事件相关电位的概念,通过平均叠加技术从头颅表面记录大脑诱发电位来反映认知过程中大脑的神经电生理改变,因为事件相关电位与认知过程有密切关系,故被认为是“窥视”心理活动的“窗口”。神经电生理技术的发展,为研究大脑认知活动过程提供了新的方法与途径。
事件相关电位(ERP)是一种特殊的脑诱发电位,通过有意地赋予刺激仪特殊的心理意义,利用多个或多样的刺激所引起的脑的电位。它反映了认知过程中大脑的神经点生理的变化,也被称为认知电位,也就是指当人们对某课题进行认知加工时,从头颅表面记录到的脑点位。经典的ERP主要成分包括P1、N1、P2、N2、P3,其中前三种称为外源性称为,而后两种称为内源性成分。这几种成分的主要特点是:首先不仅仅是大脑单纯生理活动的体现,而且反映了心理活动的某些方面;其次,它们的引出必须要有特殊的刺激安排,而且是两个以上的刺激或者是刺激的变化。其中P3是ERP中最受关注与研究的一种内源性成分,也是用于测谎的最主要指标。因此,在某种程度上,P3就成了ERP的代名词。
(二)诱发电位的特征
事件相关电位(ERP)是一种特殊的脑诱发电位,诱发电位(Evoked Potentials,EPs),也称诱发反应(Evoked Response),是指给予神经系统(从感受器到大脑皮层)特定的刺激,或使大脑对刺激(正性或负性)的信息进行加工,在该系统与脑的相应部位产生的可以检出的、与刺激有相对固定时间间隔(锁时关系)与特定位相的生物电反应。诱发电位应具备如下特征:
1.必须在特定的部位才能检测出来;
2.都有其特定的波形与电位分布;
3.诱发电位的潜伏期与刺激之间有较严格的锁时关系,在给予刺激时几乎立即或在一定时间内瞬时出现。
(三)诱发电位的分类
诱发电位的分类方法有多种,依据刺激通道分为听觉诱发电位、视觉诱发电位、体感诱发电位等;根据潜伏期长短分为早潜伏期诱发电位、中潜伏期诱发电位、晚(长)潜伏期诱发电位与慢波。临床上实用起见,将诱发电位分为两大类:与感觉或运动功能有关的外源性刺激相关电位与与认知功能有关的内源性事件相关电位(Event-Related PotentialS,ERPs)。
内源性事件相关电位与外源性刺激相关电位有着明显的不同。ERPs是在注意的基础上,与识别、比较、判断、记忆、决断等心理活动有关,反映了认知过程的不同方面,是了解大脑认知功能活动的“窗口”。经典的ERPs成分包括P1、Nl、P2、N2、P3(P300),其中P1、N1、P2为ERPs的外源性(生理性)成分,受刺激物理特性影响;N2、P3为ERPs的内源性(心理性)成分,不受刺激物理特性的影响,与被试的精神状态与注意力有关。现在ERPs的概念范围有扩大趋势,广义上讲,ERPs尚包括N4(N400)、失匹配阴性波(Mismatch NegatiVity,MMN)、伴随负反应(Contigent NegatiVe Variaeion,CNV)等。但长期以来有人通常以P3作为事件相关电位的代称,虽有失偏颇,但临床应用甚广。
(四)事件相关电位的测试方法 事件相关电位属于长潜伏期诱发电位,测试时一般要求被试者清醒,并在一定程度上参与其中。引出ERPs的刺激是按研究目的不同编制而成的不同刺激序列,包括两种及两种以上的刺激,其中一个刺激与标准刺激产生偏离,以启动被试的认知活动过程。如果由阳性的物理刺激启动,除了由认知活动产生的内源性成分,尚包括外源性刺激相关电位;如由阴性刺激来启动心理活动过程,则引出由认知加工而产生的内源性成分。
P3为ERPs中重要的内源性成分,现时对它的研究最为广泛。多为神经精神学科研究,如精神分裂症、脑血管疾病与痴呆症、智力低下等,通过研究P3的潜伏期、波幅、波形变化,反映认知障碍或智能障碍及其程度,同时尚应用于测谎研究。另有人将P3、CNV用作观察神经精神药物治疗效果的指标。事件相关电位的另一内源性成分N2为刺激以后200毫秒左右出现的负向波,反映大脑对刺激的初步加工,该波并非单一成分,而是一复合波,由N2a与N2b两部分组成,N2a不受注意的影响,反映对刺激物理特性的初步加工。
刺激模式:刺激模式的设置是研究ERPs的关键,要求根据研究目的不同设计不同的刺激模式,包括两种及以上不同概率的刺激序列,并以特定或随机方式出现。包括视觉刺激模式、听觉刺激模式、躯体感觉刺激模式。听觉刺激模式包括三类:1.随机作业(OB刺激序列);2.双随机作业;3.选择注意。OB刺激序列(oddball paradigm):通过耳机同步给高调、低调纯音,低概率音作为靶刺激,诱发ERPs。通常靶刺激概率为10—30%,非靶概率70一90%,刺激间隔多采用1.5—2秒,刺激持续时间通常为40—80毫秒,反应方式为或默数靶信号出现次数或按键反应。
(五)影响事件相关电位的因素 ▲物理因素
刺激的概率:靶刺激概率越小,P3的波幅越高,反之,波幅减小。一般靶刺激与非靶刺激的比例为20:80;刺激的时间间隔:间隔越长,P3波幅越高;刺激的感觉通道:听、视、体感感觉通道皆可引出ERPs,但其潜伏期及波幅不尽相同。
▲心理因素
事件相关电位检测过程中一般要求被试者主动参与,因而被试者的觉醒状态、注意力是否集中皆可影响结果。另外,由于被试者只有识别靶刺激并作出反应才能诱发出ERPs成分,因此,作业难度对测试结果也有影响,难度加大时,波幅降低,潜伏期延长。
▲生理因素
年龄:不同年龄P3的波幅及潜伏期不同。潜伏期与年龄呈正相关,随年龄增加而延长,而波幅与年龄呈负相关。在儿童及青少年,波幅较高;分布:ERPs各成分有不同的头皮分布。
事件相关电位(ERP)作为可以反映大脑高级思维活动的一种客观方法在研究认知功能中得到广泛的应用, 而作为其内源形成分的P300是ERP中最典型、最常用的成分与认知过程密切相关, 被视为“窥视”心理活动的一个窗口,并认为它是脑研究的一种新型手段。
事件相关电位具有高时间分辨率的特点,使其在揭示认知的时间过程方面极具优势,能锁时性的反映认知的动态过程.该方法已经成为研究脑认知活动的重要手段.P300是较早发现的内源性事件相关电位成分,主要与人在从事某一任务时的认知活动如:注意、辨别、及工作记忆有关。P300可能代表期待的感觉信息得到确认与知觉任务的结束,目前已被广泛用来研究认知功能。其潜伏期反映对刺激物评价或归类所需要的时间即反应速度,随作业难度的增加而延长,而波幅反映了心理负荷的量,即被试投入到任务重的脑力资源的多少。虽然P300对认知损害评价的临床应用较广,但近年来的研究证实P300的脑内源不止一个,而是与多种认知加工有关,所以其在认知损害特征的精确描述方面有一定的局限性。
第三项
脑成像
脑成像就是通过最新技术使得神经科学家可以“看到活体脑的内部”。这些脑成像方法可以在以下方面为神经科学家提供帮助:理解脑特定区域与其功能之间的关系。对受神经疾病影响的脑区进行定位。发明新方法治疗脑部疾病。
脑成像包括:
(一)计算机X线断层摄影(CT扫描)CT扫描时,一束X射线穿过头部,感光胶片形成图像。这种方法可以产生脑部剖面成像。这种方法只显示脑结构,而非脑功能。
(二)正电子发射断层扫描术(PET)扫描仪通过检测被注射入或被吸入的放射物可以产生脑图像。经常使用的放射性物质包括氧,氟利昂,碳与氮。这些物质进入血液后被输送到使用这些物质的脑区。于是,氧与葡萄糖就会积聚在新陈代谢较活跃的脑区。放射性物质衰变时会发射出一个中子与一个正电子。当正电子撞击电子时,两者都被破坏,放射出两道伽玛射线。伽玛射线检测器记录下发出伽玛射线的脑区。这种方法提供了脑的功能视图。优点:
1、提供了脑活动的图像。缺点:
1、价格昂贵;
2、使用放射性物质
(三)磁共振成像(MRI)磁共振成像使用无线电频率信号检测,信号产生于磁场中转移的无线电波。它提供了脑的解剖视图。优点:
1、没有 X-射线或放射物质;
2、提供详细的不同维度的脑图像;
3、安全无痛,非侵入性;
4、病人无需做特殊准备(除了去除所有金属物品),病人之前可以进食。缺点:
1、价格昂贵。
2、不适用于带有金属物品的病人,如起搏器。
3、不适用于不配合的病人,因为病人必须安静地平躺。
4、不适用于患有幽闭恐怖症的病人(害怕狭小地方),但现在已出现设计更加宽敞的新型磁共振系统。
(四)功能磁共振成像(fMRI)
功能磁共振成像对流向特定脑区的血液的变化进行检测。它同时提供脑的解剖与功能视图。
(五)血管造影术 在染料被注入血液中后,血管造影术使用一束X射线。这种方法可以提供脑血管图像。
还有就是 SPECT 单光子发射计算机断层成像术(Single-Photon Emission Computed Tomography,SPECT)与正电子发射断层成像术(Positron Emission Tomography,PET)是核医学的两种CT技术,由于它们都是对从病人体内发射的γ射线成像,故统称发射型计算机断层成像术(Emission Computed Tomography,ECT)。
SPECT的基本本成像原理是:γ照相机探头的每个灵敏点探测沿一条投影线(Ray)进来的γ光子,其测量值代表人体在该投影线上的放射性之与。在同一条直线上的灵敏点可探测人体一个断层上的放射性药物,它们的输出称作该断层的一维投影(Projection)。图中各条投影线都垂直于探测器并互相平行,故称之为平行束,探测器的法线与X轴的交角θ称为观测角(View)。γ照相机是二维探测器,安装了平行孔准直器后,可以同时获取多个断层的平行束投影,这就是平片。平片表现不出投影线上各点的前后关系。要想知道人体在纵深方向上的结构,就需要从不同角度进行观测。可以证明,知道了某个断层在所有观测角的一维投影,就能计算出该断层的图像。从投影求解断层图像的过程称作重建(Reconstruction)。这种断层成像术离不开计算机,所以称作计算机断层成像术(Computered Tomography,CT)。CT设备的主要功能是获取投影数据与重建断层图像。
ECT显像的主要临床应用
1、骨骼显像。
骨骼显像是早期诊断恶性肿瘤骨转移的首选方法。可进行疾病分期、骨痛评价、预后判断、疗效观察与探测病理骨折的危险部位。
2、心脏灌注断层显像
心肌缺血的诊断。可评价冠状动脉病变范围,对冠心病危险性进行分级;评价冠脉狭窄引起的心肌血流灌注量改变及侧枝循环的功能,评价心肌细胞活力;对心肌梗塞的预后评价与疗效观察;观察心脏搭桥术及介入性治疗后心肌缺血改善情况。
心肌梗死的诊断,心梗伴缺血的诊断,判断心肌细胞存活情况。
心肌病、室壁瘤的鉴别诊断。
3、甲状腺显像
异位甲状腺的诊断与定位。具有独特价值。
甲状腺结节功能的判断与良恶性鉴别,具有较高诊断价值。
高分化甲状腺癌转移灶的定位与诊断。
甲状腺大小与重量的估计。
4、局部脑血流断层显像
缺血性脑血管意外的诊断。具有较高诊断价值。
癫痫致痫灶的定位诊断。癫痫发作间期的阳性率高达60%(而XCT与MRI的阳性率约25%)。
判断脑肿瘤的血运,鉴别术后或放疗后复发与瘢痕。
痴呆分型。尤其对早老性痴呆(Alzheimer病)的诊断有较高价值。
5、肾动态显像及肾图检查。
了解肾动脉病变及双肾血供情况;对肾功能及分肾功能的判断;了解上尿路通畅情况及对尿路梗阻的诊断;监测移植肾血流灌注与功能情况;以及了解糖尿病对肾功能的影响。其它显像的主要临床应用
甲状旁腺显像:对甲状旁腺腺瘤的诊断与定位。
肾上腺髓质显像:对嗜铬细胞瘤及其转移灶的诊断及定位,及恶性嗜铬细胞瘤131I-MIBG治疗后随访。
肺灌注显像与肺通气显像:对肺动脉血栓栓塞症的诊断与疗效判断。
肝脏胶体显像、肝血流与肝血池显像:对肝海绵状血管瘤的诊断。
肝胆动态显像:用于鉴别梗阻性黄疸与肝细胞性黄疸;鉴别先天性胆道闭锁与婴肝综合征及疗效观察。
肠道出血显像:最适用于探测胃以下、乙状结肠以上的活动性下消化道出血。
异位胃粘膜显像:对美克尔憩室的诊断及定位,对肠梗阻或肠套叠(怀疑与美克尔憩室或小肠重复畸形有关)的鉴别诊断。
[编辑本段]事件相关电位
ERP反映了认知过程中大脑的神经点生理的变化,也被称为认知电位,也就是指当人们对某课题进行认知加工时,从头颅表面记录到的脑点位[3]。
提问者: zhtao_deutsch-一级最佳答案病人的疑虑带有很大的普遍性,很多病人总是担心医生给病人做检查是不是必要的,是不是在重复收费检查等等。
1、MRI(磁共振成像血管造影)诊断技术,完全脱离了利用X线吸收成像的原理,对于病人没有放射性伤害,MRI的成像原理是体内H质子的信号,经过计算机处理后得到的图像。
头颅磁共振成像(MRI)检查较CT更为敏感,具有多方向切层、多参数成像的特点,能更精确地显示病变部位、范围大小及组织学特性,是发现脑内部结构病变的首选方法,但价格较为昂贵。
2、脑电图检查的作用及意义
人体的大脑有140亿个脑细胞,其中有2.5亿个神经细胞。神经细胞活动时可以产生各种生物电信号,脑电图就是利用脑电图机记录人体大脑生物电的信息的。只要将脑电图机的探测仪电极贴在头皮上,仪器就能收到脑电活动整个过程中电位的变化,这时扫描笔便在移动着的图纸上描绘出各种曲线。由于曲线的频率与振幅不同,就构成了不同的波形,形成了脑电图波。
一般来说,每个人的脑电图都有其固有的特征。脑电图波分为慢活动波与快活动波,在正常生理条件下,有着正常的生理节律与固有特征,而当脑电图出现异常时,则提示有病变的可能。因此,在对大脑生理功能进行评判时,可以进行脑电图检查。由于脑电图是一种无创伤性的检查方法,所以可以多次进行重复检查。
3、脑血流图又叫脑电阻图,它是利用电阻变化的原理,描记随心脏跳动而变化的脑血流波动图形。脑血流图比较能够客观地反映脑血管的紧张度与血管的弹性变化,对判断脑血管病有一定的参考价值。
以上的检查项目各有各的用途,一般不能相互替代。
第二篇:ERP与SCM的区别
理论模型和方法
多年来,人们一直在坚持不懈地将数学规划等方法用于企业安排计划和管理上,渐渐形成了MRP法和数学规划法这两种主要的计划编制方法,后者由于受到如建模困难、运算量大等客观条件的限制,发展比较迟缓,而MRP法则由于省略了一些实际存在、但又难以求解的因素,变得简单易算,获得了较好的成效和发展。但是,人们逐渐发现那些因素往往是不容忽视的,加之计算机计算速度的飞速发展,使得采用数学规划方法不再困难,人们开始以常驻计算机内存的方式采用数学规划方法编制计划,这种全新的计划方法就是SCM的开端。
SCM采用了多种数学解析的优化模型和规则,是基于约束理论进行计划的,它考虑了物料、设备、人员、场所、时间和技术等所有的约束因素,对不同的目标可以通过不同的规制进行优化。而ERP的理论模型过于简单和陈旧,它的计划模型和提前期的计算方法等都无法模拟今日复杂多变的业务过程。例如,ERP作计划的前提假设是企业具有无限的物料和能力,计划模型是无约束的;又如,ERP一般采用简单的线性公式T= A+BX计算产品的提前期,但每种物品每一次的采购提前期都可能存在较大差别,无法用这种简单公式来准确计算。
管理范围
ERP只能对其内部资源进行管理。然而,单靠企业内部的业务改进所获得的收效已变得越来越有限,企业逐渐将管理焦点转移到超越企业之外的供应链管理和上下游的业务协同上。SCM则能够满足供应链横向一体化运作的要求,在考虑了资源约束、优化和决策的技术支持下,有效利用和整合外部资源。
SCM另一个优于ERP之处是它能够模拟和改善财务指标,特别是收入、成本和资产利用率指标。它不仅仅是简单地降低成本,而是利用不同的方式来满足市场和客户的需求。
编制计划
SCM扩大了计划范围,通过不同的规则对不同业务进行计划,并可对单一目标和多目标进行优化的计划;而且,SCM的计划是并发的、计划时段是连续的,综合、完整地考虑了约束问题,生成的提前期是弹性的,可以对供应链和企业的各项业务进行计划,一次性地考虑业务流程的纵向和横向的协调,无需一个个地依次制订计划。而ERP编制计划是按顺序进行的,计划时段是离散的,生成的提前期是固定的,仅面向某一功能的计划,计划的能见度只限于局部,几乎不考虑约束。
同时,SCM的计划覆盖了所有的业务,计划模型可以做得足够的详细,覆盖了长、中、短周期,可以实现倒排、顺排和中间排,其精细程度可从年、月、周一直到天、小时和分钟。SCM还能够随时根据生产和客户需求的变化进行重排计划,量化地反映甚至超前于市场的需求,例如变化的资源和约束,用户的优先权等。它可以实现一个可持续转变的流程,使得重排计划能够对每一次意外变化进行随时处理。而ERP有时按天做计划都很困难,更无法精确到小时和分钟,也很难做到快速地重排计划。最后,在生成计划后,SCM可以根据“评价计划成本”标准评价计划成本,并与企业的财务指标进行对比和衡量,进一步核实其可行性,但ERP无法对做出的计划成本进行评价。业务管理
SCM在业务管理上具有比ERP更好、更多的功能。
首先,SCM具有极强的实时承诺性,它的承诺标准能为客户提供准确的交货日期。虽然ERP的可用量检查ATP也具有某种承诺能力,但仅是建立在对现有库存检查的基础上,而 SCM在ATP的基础上,还通过对需求承诺能力和对订单承诺能力的检查、扩展的生产可用性检查和对获利能力的检查等功能,以对客户做出准确的交货承诺,并在商谈订单的第一时间就能确定该订单是否能够获利;
第二,SCM能对供应链上的资源进行优化调配,将供应链上的某种稀缺资源预先分派给具有较高优先级别的客户或渠道的需求;
第三,SCM的计划范围扩展到了企业之外,能生成跨企业的协同计划,实时了解伙伴们的业务变化情况,及时进行重排计划,保持高度的灵活性和预见性,以快速响应市场需求,ERP则无法满足这种需求;
第四,SCM可以动态计算提前期,它的提前期标准提供了一个优于ERP的特性,ERP逻辑使用固定的提前期进行计划,这对整个供应链运行具有若干负面的影响;
第五,SCM可以对供应链的需求、供给和约束进行监控,实时地将这三者进行比较,一旦出现不匹配时立刻发出预警信号,并执行智能的逻辑操作使它们重新恢复平衡,重新达到同步,这也是ERP无法实现的。
总的来说,ERP只能告诉你应该做什么,而SCM则能帮助你决定怎样去做。例如,供应商和客户的位置变化将怎样影响运输成本?增加或取消一个配送中心,或越库作业将如何影响运输成本?这些都是ERP无法解答,而必须让位给SCM来解决的问题。
事务处理虽然SCM具有众多的计划、优化和决策功能,但它却不具备某些事务处理和数据维护功能,例如,货物的接收、盘点与出库,工单/采购单发放、发票 /文档管理、会计管理,对项目主文件/BOM、列表进行维护等。而ERP则可以完成这些功能。目前,有些SCM系统也扩展了它的功能,增加了供应链执行系统SCE,由该系统来完成上述工作。
第三篇:MES与ERP的区别
天智MES与用友ERP的区别与补充
ERP 是MES的上一级管理系统。MES 是叫做制造执行系统,属于企业管理中的关乎生产制造细节的管理层,但是又不具体参与生产制造的环节。MES是位于上层计划管理系统与底层工业控制之间,面向车间层的管理系统。
主要区别:
1、虽然同属软件系统、平台管理同属用于辅助企业管理,但MES是ERP的延升。
2、MES是软硬件集成系统,而ERP是纯软件系统,缺少硬件设备的支持。
3、MES管理的是车间现场与实物的管理系统,而ERP是管理信息数据系统。
4、MES依靠条形码设备自动采集完工数量等信息,而ERP全靠人工输入,工作量大,易出错。
MES是通过信息传递,对从订单下达到产品完成整个的生产过程进行优化管理。当工厂里有实时事件发生时,MES能对此及时做出反应、报告,并用当前的准确数据对它们进行指导和处理。从而使其既能提高工厂及时交货能力、改善物料的流通性能又提高生产回报率。
目前国际上所有流程企业,以及国内石化、钢铁等行业均广泛应用MES+ERP模式来规划和发展企业信息系统。
ERP无法实现的功能需MES作为补充:
1、出现用户产品投诉的时候,能否根据产品文字号码追溯这批产品的所有生产过程信息?能否立即查明它的:原料供应商、操作机台、操作人员、经过的工序、生产时间日期和关键的工艺参数?
2、同一条生产线需要混合组装多种型号产品的时候,能否自动校验和操作提示以防止工人部件装配错误、产品生产流程错误、产品混装和货品交接错误?
3、过去12小时之内生产线上出现最多的5种产品缺陷是什么?次品数量各是多少?
4、目前仓库以及前工序、中工序、后工序线上的每种产品数量各是多少?要分别供应给哪些供应商?何时能够及时交货?
5、生产线和加工设备有多少时间在生产,多少时间在停转和空转?影响设备生产潜能的最主要原因是:设备故障?调度失误?材料供应不及时?工人培训不够?还是工艺指标不合理?
6、能否对产品的质量检测数据自动进行统计和分析,精确区分产品质量的随机波动与异常波动,将质量隐患消灭于萌芽之中?
7、能否废除人工报表,自动统计每个过程的生产数量、合格率和缺陷代码?
第四篇:ERP与MRP2的区别
ERP同MRPⅡ的主要区别
·在资源管理范围方面的差别
MRPII主要侧重对企业内部人、财、物等资源的管理,ERP系统在MRPII的基础上扩展了管理范围,它把客户需求和企业内部的制造活动、以及供应商的制造资源整合在一起,形成企业一个完整的供应链并对供应链上所有环节如订单、采购、库存、计划、生产制造、质量控制、运输、分销、服务与维护、财务管理、人事管理、实验室管理、项目管理、配方管理等进行有效管理。
·在生产方式管理方面的差别
MRPII系统把企业归类为几种典型的生产方式进行管理,如重复制造、批量生产、按订单生产、按订单装配、按库存生产等,对每一种类型都有一套管理标准。而在80年代末、90年代初期,为了紧跟市场的变化,多品种、小批量生产以及看板式生产等则是企业主要采用的生产方式,由单一的生产方式向混合型生产发展,ERP则能很好地支持和管理混合型制造环境,满足了企业的这种多角化经营需求。
·在管理功能方面的差别
ERP除了MRPII系统的制造、分销、财务管理功能外,还增加了支持整个供应链上物料流通体系中供、产、需各个环节之间的运输管理和仓库管理;支持生产保障体系的质量管理、实验室管理、设备维修和备品备件管理;支持对工作流(业务处理流程)的管理。·在事务处理控制方面的差别
MRPII是通过计划的及时滚动来控制整个生产过程,它的实时性较差,一般只能实现事中控制。而ERP系统支持在线分析处理OLAP(OnlineAnalyticalProcessing)、售后服务即质量反馈,强调企业的事前控制能力,它可以将设计、制造、销售、运输等通过集成来并行地进行各种相关的作业,为企业提供了对质量、适应变化、客户满意、绩效等关键问题的实时分析能力。此外,在MRPII中,财务系统只是一个信息的归结者,它的功能是将供、产、销中的数量信息转变为价值信息,是物流的价值反映。而ERP系统则将财务计划和价值控制功能集成到了整个供应链上。
·在跨国(或地区)经营事务处理方面的差别
现在企业的发展,使得企业内部各个组织单元之间、企业与外部的业务单元之间的协调变得越来越多和越来越重要,ERP系统应用完整的组织架构,从而可以支持跨国经营的多国家地区、多工厂、多语种、多币制应用需求。
·在计算机信息处理技术方面的差别
随着IT技术的飞速发展,网络通信技术的应用,使得ERP系统得以实现对整个供应链信息进行集成管理。ERP系统采用客户/服务器(C/S)体系结构和分布式数据处理技术,支持Internet/Intranet/Extranet、电子商务(E-business、E-commerce)、电子数据交换(EDI)。此外,还能实现在不同
第五篇:委托与事件的区别
委托与事件的区别
委托 和 事件在.Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里别(biè)得慌,混身不自在。本文中,我将通过两个范例由浅入深地讲述什么是委托、为什么要使用委托、事件的由来、.Net Framework中的委托和事件、委托和事件对Observer设计模式的意义,对它们的中间代码也做了讨论。
将方法作为方法的参数
我们先不管这个标题如何的绕口,也不管委托究竟是个什么东西,来看下面这两个最简单的方法,它们不过是在屏幕上输出一句问候的话语: public void GreetPeople(string name){ // 做某些额外的事情,比如初始化之类,此处略 EnglishGreeting(name);} public void EnglishGreeting(string name){ Console.WriteLine(“Morning, ” + name);}
暂且不管这两个方法有没有什么实际意义。GreetPeople用于向某人问好,当我们传递代表某人姓名的name参数,比如说“Jimmy”,进去的时候,在这个方法中,将调用EnglishGreeting方法,再次传递name参数,EnglishGreeting则用于向屏幕输出 “Morning, Jimmy”。
现在假设这个程序需要进行全球化,哎呀,不好了,我是中国人,我不明白“Morning”是什么意思,怎么办呢?好吧,我们再加个中文版的问候方法:
public void ChineseGreeting(string name){ Console.WriteLine(“早上好, ” + name);}
这时候,GreetPeople也需要改一改了,不然如何判断到底用哪个版本的Greeting问候方法合适呢?在进行这个之前,我们最好再定义一个枚举作为判断的依据:
public enum Language{ English, Chinese } public void GreetPeople(string name, Language lang){ //做某些额外的事情,比如初始化之类,此处略 swith(lang){ case Language.English: EnglishGreeting(name);break;case Language.Chinese: ChineseGreeting(name);break;} }
OK,尽管这样解决了问题,但我不说大家也很容易想到,这个解决方案的可扩展性很差,如果日后我们需要再添加韩文版、日文版,就不得不反复修改枚举和GreetPeople()方法,以适应新的需求。
在考虑新的解决方案之前,我们先看看 GreetPeople的方法签名:
public void GreetPeople(string name, Language lang)
我们仅看 string name,在这里,string 是参数类型,name 是参数变量,当我们赋给name字符串“jimmy”时,它就代表“jimmy”这个值;当我们赋给它“张子阳”时,它又代表着“张子阳”这个值。然后,我们可以在方法体内对这个name进行其他操作。哎,这简直是废话么,刚学程序就知道了。
如果你再仔细想想,假如GreetPeople()方法可以接受一个参数变量,这个变量可以代表另一个方法,当我们给这个变量赋值 EnglishGreeting的时候,它代表着 EnglsihGreeting()这个方法;当我们给它赋值ChineseGreeting 的时候,它又代表着ChineseGreeting()方法。我们将这个参数变量命名为 MakeGreeting,那么不是可以如同给name赋值时一样,在调用 GreetPeople()方法的时候,给这个MakeGreeting 参数也赋上值么(ChineseGreeting或者EnglsihGreeting等)?然后,我们在方法体内,也可以像使用别的参数一样使用MakeGreeting。但是,由于MakeGreeting代表着一个方法,它的使用方式应该和它被赋的方法(比如ChineseGreeting)是一样的,比如: MakeGreeting(name);
好了,有了思路了,我们现在就来改改GreetPeople()方法,那么它应该是这个样子了:
public void GreetPeople(string name, *** MakeGreeting){ MakeGreeting(name);}
注意到 ***,这个位置通常放置的应该是参数的类型,但到目前为止,我们仅仅是想到应该有个可以代表方法的参数,并按这个思路去改写GreetPeople方法,现在就出现了一个大问题:这个代表着方法的MakeGreeting参数应该是什么类型的?
NOTE:这里已不再需要枚举了,因为在给MakeGreeting赋值的时候动态地决定使用哪个方法,是ChineseGreeting还是 EnglishGreeting,而在这个两个方法内部,已经对使用“morning”还是“早上好”作了区分。
聪明的你应该已经想到了,现在是委托该出场的时候了,但讲述委托之前,我们再看看MakeGreeting参数所能代表的 ChineseGreeting()和EnglishGreeting()方法的签名:
public void EnglishGreeting(string name)public void ChineseGreeting(string name)
如同name可以接受String类型的“true”和“1”,但不能接受bool类型的true和int类型的1一样。MakeGreeting的 参数类型定义 应该能够确定 MakeGreeting可以代表的 方法种类,再进一步讲,就是MakeGreeting可以代表的方法 的 参数类型和返回类型。
于是,委托出现了:它定义了MakeGreeting参数所能代表的方法的种类,也就是MakeGreeting参数的类型。
NOTE:如果上面这句话比较绕口,我把它翻译成这样:string 定义了name参数所能代表的值的种类,也就是name参数的类型。
本例中委托的定义:
public delegate void GreetingDelegate(string name);
可以与上面EnglishGreeting()方法的签名对比一下,除了加入了delegate关键字以外,其余的是不是完全一样?
现在,让我们再次改动GreetPeople()方法,如下所示:
public void GreetPeople(string name, GreetingDelegate MakeGreeting){ MakeGreeting(name);}
如你所见,委托GreetingDelegate出现的位置与 string相同,string是一个类型,那么GreetingDelegate应该也是一个类型,或者叫类(Class)。但是委托的声明方式和类却完全不同,这是怎么一回事?实际上,委托在编译的时候确实会编译成类。因为Delegate是一个类,所以在任何可以声明类的地方都可以声明委托。更多的内容将在下面讲述,现在,请看看这个范例的完整代码: 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(“Morning, ” + name);}
private static void ChineseGreeting(string name){ Console.WriteLine(“早上好, ” + name);}
//注意此方法,它接受一个GreetingDelegate类型的方法作为参数
private static void GreetPeople(string name, GreetingDelegate MakeGreeting){ MakeGreeting(name);}
static void Main(string[] args){ GreetPeople(“Jimmy Zhang”, EnglishGreeting);GreetPeople(“张子阳”, ChineseGreeting);Console.ReadKey();} } }
输出如下:
Morning, Jimmy Zhang 早上好, 张子阳
我们现在对委托做一个总结:
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。将方法绑定到委托
看到这里,是不是有那么点如梦初醒的感觉?于是,你是不是在想:在上面的例子中,我不一定要直接在GreetPeople()方法中给 name参数赋值,我可以像这样使用变量:
static void Main(string[] args){ string name1, name2;name1 = “Jimmy Zhang”;name2 = “张子阳”;
GreetPeople(name1, EnglishGreeting);GreetPeople(name2, ChineseGreeting);Console.ReadKey();}
而既然委托GreetingDelegate 和 类型 string 的地位一样,都是定义了一种参数类型,那么,我是不是也可以这么使用委托?
static void Main(string[] args){ GreetingDelegate delegate1, delegate2;delegate1 = EnglishGreeting;delegate2 = ChineseGreeting;
GreetPeople(“Jimmy Zhang”, delegate1);GreetPeople(“张子阳”, delegate2);Console.ReadKey();}
如你所料,这样是没有问题的,程序一如预料的那样输出。这里,我想说的是委托不同于string的一个特性:可以将多个方法赋给同一个委托,或者叫将多个方法绑定到同一个委托,当调用这个委托的时候,将依次调用其所绑定的方法。在这个例子中,语法如下:
static void Main(string[] args){ GreetingDelegate delegate1;delegate1 = EnglishGreeting;// 先给委托类型的变量赋值
delegate1 += ChineseGreeting;// 给此委托变量再绑定一个方法
// 将先后调用 EnglishGreeting 与 ChineseGreeting 方法 GreetPeople(“Jimmy Zhang”, delegate1);Console.ReadKey();}
输出为:
Morning, Jimmy Zhang 早上好, Jimmy Zhang
实际上,我们可以也可以绕过GreetPeople方法,通过委托来直接调用EnglishGreeting和ChineseGreeting:
static void Main(string[] args){ GreetingDelegate delegate1;delegate1 = EnglishGreeting;// 先给委托类型的变量赋值
delegate1 += ChineseGreeting;// 给此委托变量再绑定一个方法
// 将先后调用 EnglishGreeting 与 ChineseGreeting 方法 delegate1(“Jimmy Zhang”);Console.ReadKey();}
NOTE:这在本例中是没有问题的,但回头看下上面GreetPeople()的定义,在它之中可以做一些对于EnglshihGreeting和ChineseGreeting来说都需要进行的工作,为了简便我做了省略。
注意这里,第一次用的“=”,是赋值的语法;第二次,用的是“+=”,是绑定的语法。如果第一次就使用“+=”,将出现“使用了未赋值的局部变量”的编译错误。
我们也可以使用下面的代码来这样简化这一过程:
GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);delegate1 += ChineseGreeting;// 给此委托变量再绑定一个方法
看到这里,应该注意到,这段代码第一条语句与实例化一个类是何其的相似,你不禁想到:上面第一次绑定委托时不可以使用“+=”的编译错误,或许可以用这样的方法来避免:
GreetingDelegate delegate1 = new GreetingDelegate();delegate1 += EnglishGreeting;// 这次用的是 “+=”,绑定语法。delegate1 += ChineseGreeting;// 给此委托变量再绑定一个方法
但实际上,这样会出现编译错误: “GreetingDelegate”方法没有采用“0”个参数的重载。尽管这样的结果让我们觉得有点沮丧,但是编译的提示:“没有0个参数的重载”再次让我们联想到了类的构造函数。我知道你一定按捺不住想探个究竟,但再此之前,我们需要先把基础知识和应用介绍完。
既然给委托可以绑定一个方法,那么也应该有办法取消对方法的绑定,很容易想到,这个语法是“-=”:
static void Main(string[] args){ GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);delegate1 += ChineseGreeting;// 给此委托变量再绑定一个方法
// 将先后调用 EnglishGreeting 与 ChineseGreeting 方法 GreetPeople(“Jimmy Zhang”, delegate1);Console.WriteLine();
delegate1-= EnglishGreeting;//取消对EnglishGreeting方法的绑定 // 将仅调用 ChineseGreeting GreetPeople(“张子阳”, delegate1);Console.ReadKey();} 输出为:
Morning, Jimmy Zhang 早上好, Jimmy Zhang
早上好, 张子阳
让我们再次对委托作个总结:
使用委托可以将多个方法绑定到同一个委托变量,当调用此变量时(这里用“调用”这个词,是因为此变量代表一个方法),可以依次调用所有绑定的方法。
事件的由来
我们继续思考上面的程序:上面的三个方法都定义在Programe类中,这样做是为了理解的方便,实际应用中,通常都是 GreetPeople 在一个类中,ChineseGreeting和 EnglishGreeting 在另外的类中。现在你已经对委托有了初步了解,是时候对上面的例子做个改进了。假设我们将GreetingPeople()放在一个叫GreetingManager的类中,那么新程序应该是这个样子的:
namespace Delegate { //定义委托,它定义了可以代表的方法的类型 public delegate void GreetingDelegate(string name);
//新建的GreetingManager类 public class GreetingManager{ public void GreetPeople(string name, GreetingDelegate MakeGreeting){ MakeGreeting(name);} }
class Program { private static void EnglishGreeting(string name){ Console.WriteLine(“Morning, ” + name);}
private static void ChineseGreeting(string name){ Console.WriteLine(“早上好, ” + name);}
static void Main(string[] args){ //......} } }
这个时候,如果要实现前面演示的输出效果,Main方法我想应该是这样的:
static void Main(string[] args){ GreetingManager gm = new GreetingManager();gm.GreetPeople(“Jimmy Zhang”, EnglishGreeting);gm.GreetPeople(“张子阳”, ChineseGreeting);}
我们运行这段代码,嗯,没有任何问题。程序一如预料地那样输出了: Morning, Jimmy Zhang 早上好, 张子阳
现在,假设我们需要使用上一节学到的知识,将多个方法绑定到同一个委托变量,该如何做呢?让我们再次改写代码:
static void Main(string[] args){ GreetingManager gm = new GreetingManager();GreetingDelegate delegate1;delegate1 = EnglishGreeting;delegate1 += ChineseGreeting;gm.GreetPeople(“Jimmy Zhang”, delegate1);} 输出:
Morning, Jimmy Zhang 早上好, Jimmy Zhang
到了这里,我们不禁想到:面向对象设计,讲究的是对象的封装,既然可以声明委托类型的变量(在上例中是delegate1),我们何不将这个变量封装到 GreetManager类中?在这个类的客户端中使用不是更方便么?于是,我们改写GreetManager类,像这样:
public class GreetingManager{ //在GreetingManager类的内部声明delegate1变量 public GreetingDelegate delegate1;
public void GreetPeople(string name, GreetingDelegate MakeGreeting){ MakeGreeting(name);} }
现在,我们可以这样使用这个委托变量:
static void Main(string[] args){ GreetingManager gm = new GreetingManager();gm.delegate1 = EnglishGreeting;gm.delegate1 += ChineseGreeting;
gm.GreetPeople(“Jimmy Zhang”, gm.delegate1);}
尽管这样达到了我们要的效果,但是似乎并不美气,光是第一个方法注册用“=”,第二个用“+=”就让人觉得别扭。此时,轮到Event出场了,C# 中可以使用事件来专门完成这项工作,我们改写GreetingManager类,它变成了这个样子:
public class GreetingManager{ //这一次我们在这里声明一个事件
public event GreetingDelegate MakeGreet;
public void GreetPeople(string name, GreetingDelegate MakeGreeting){ MakeGreeting(name);} }
很容易注意到:MakeGreet 事件的声明与之前委托变量delegate1的声明唯一的区别是多了一个event关键字。看到这里,你差不多明白到:事件其实没什么不好理解的,声明一个事件不过类似于声明一个委托类型的变量而已。
我们想当然地改写Main方法: static void Main(string[] args){ GreetingManager gm = new GreetingManager();gm.MakeGreet = EnglishGreeting;// 编译错误1 gm.MakeGreet += ChineseGreeting;
gm.GreetPeople(“Jimmy Zhang”, gm.MakeGreet);//编译错误2 }
这次,你会得到编译错误:事件“Delegate.GreetingManager.MakeGreet”只能出现在 += 或-= 的左边(从类型“Delegate.GreetingManager”中使用时除外)。
事件和委托的编译代码
这时候,我们不得不注释掉编译错误的行,然后重新进行编译,再借助Reflactor来对 event的声明语句做一探究,看看为什么会发生这样的错误:
public event GreetingDelegate MakeGreet;
可以看到,实际上尽管我们在GreetingManager里将 MakeGreet 声明为public,但是,实际上MakeGreet会被编译成 私有字段,难怪会发生上面的编译错误了,因为它根本就不允许在GreetingManager类的外面以赋值的方式访问。
我们进一步看下MakeGreet所产生的代码:
private GreetingDelegate MakeGreet;//对事件的声明 实际是 声明一个私有的委托变量
[MethodImpl(MethodImplOptions.Synchronized)] public void add_MakeGreet(GreetingDelegate value){ this.MakeGreet =(GreetingDelegate)Delegate.Combine(this.MakeGreet, value);}
[MethodImpl(MethodImplOptions.Synchronized)] public void remove_MakeGreet(GreetingDelegate value){ this.MakeGreet =(GreetingDelegate)Delegate.Remove(this.MakeGreet, value);}
现在已经很明确了:MakeGreet 事件确实是一个GreetingDelegate类型的委托,只不过不管是不是声明为public,它总是被声明为private。另外,它还有两个方法,分别是add_MakeGreet和remove_MakeGreet,这两个方法分别用于注册委托类型的方法和取消注册,实际上也就是: “+= ”对应 add_MakeGreet,“-=”对应remove_MakeGreet。而这两个方法的访问限制取决于声明事件时的访问限制符。
在add_MakeGreet()方法内部,实际上调用了System.Delegate的Combine()静态方法,这个方法用于将当前的变量添加到委托链表中。我们前面提到过两次,说委托实际上是一个类,在我们定义委托的时候:
public delegate void GreetingDelegate(string name);
当编译器遇到这段代码的时候,会生成下面这样一个完整的类: public class GreetingDelegate:System.MulticastDelegate{
public GreetingDelegate(object @object, IntPtr method);public virtual IAsyncResult BeginInvoke(string name, AsyncCallback callback, object @object);public virtual void EndInvoke(IAsyncResult result);public virtual void Invoke(string name);}
关于这个类的更深入内容,可以参阅《CLR Via C#》等相关书籍,这里就不再讨论了。委托、事件与Observer设计模式
范例说明
上面的例子已不足以再进行下面的讲解了,我们来看一个新的范例,因为之前已经介绍了很多的内容,所以本节的进度会稍微快一些:
假设我们有个高档的热水器,我们给它通上电,当水温超过95度的时候:
1、扬声器会开始发出语音,告诉你水的温度;
2、液晶屏也会改变水温的显示,来提示水已经快烧开了。
现在我们需要写个程序来模拟这个烧水的过程,我们将定义一个类来代表热水器,我们管它叫:Heater,它有代表水温的字段,叫做temperature;当然,还有必不可少的给水加热方法BoilWater(),一个发出语音警报的方法MakeAlert(),一个显示水温的方法,ShowMsg()。
namespace Delegate { class Heater { private int temperature;// 水温
// 烧水
public void BoilWater(){ for(int i = 0;i <= 100;i++){ temperature = i;
if(temperature > 95){ MakeAlert(temperature);ShowMsg(temperature);} } }
// 发出语音警报
private void MakeAlert(int param){ Console.WriteLine(“Alarm:嘀嘀嘀,水已经 {0} 度了:” , param);}
// 显示水温
private void ShowMsg(int param){ Console.WriteLine(“Display:水快开了,当前温度:{0}度。” , param);} }
class Program { static void Main(){ Heater ht = new Heater();ht.BoilWater();} } }
Observer设计模式简介
上面的例子显然能完成我们之前描述的工作,但是却并不够好。现在假设热水器由三部分组成:热水器、警报器、显示器,它们来自于不同厂商并进行了组装。那么,应该是热水器仅仅负责烧水,它不能发出警报也不能显示水温;在水烧开时由警报器发出警报、显示器显示提示和水温。
这时候,上面的例子就应该变成这个样子: // 热水器
public class Heater { private int temperature;
// 烧水
private void BoilWater(){ for(int i = 0;i <= 100;i++){ temperature = i;} } }
// 警报器
public class Alarm{ private void MakeAlert(int param){ Console.WriteLine(“Alarm:嘀嘀嘀,水已经 {0} 度了:” , param);} }
// 显示器
public class Display{ private void ShowMsg(int param){ Console.WriteLine(“Display:水已烧开,当前温度:{0}度。” , param);} }
这里就出现了一个问题:如何在水烧开的时候通知报警器和显示器?在继续进行之前,我们先了解一下Observer设计模式,Observer设计模式中主要包括如下两类对象:
Subject:监视对象,它往往包含着其他对象所感兴趣的内容。在本范例中,热水器就是一个监视对象,它包含的其他对象所感兴趣的内容,就是temprature字段,当这个字段的值快到100时,会不断把数据发给监视它的对象。
Observer:监视者,它监视Subject,当Subject中的某件事发生的时候,会告知Observer,而Observer则会采取相应的行动。在本范例中,Observer有警报器和显示器,它们采取的行动分别是发出警报和显示水温。
在本例中,事情发生的顺序应该是这样的:
1.警报器和显示器告诉热水器,它对它的温度比较感兴趣(注册)。2.热水器知道后保留对警报器和显示器的引用。
3.热水器进行烧水这一动作,当水温超过95度时,通过对警报器和显示器的引用,自动调用警报器的MakeAlert()方法、显示器的ShowMsg()方法。
类似这样的例子是很多的,GOF对它进行了抽象,称为Observer设计模式:Observer设计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新。Observer模式是一种松耦合的设计模式。实现范例的Observer设计模式
我们之前已经对委托和事件介绍很多了,现在写代码应该很容易了,现在在这里直接给出代码,并在注释中加以说明。
using System;using System.Collections.Generic;using System.Text;
namespace Delegate { // 热水器
public class Heater { private int temperature;public delegate void BoilHandler(int param);//声明委托 public event BoilHandler BoilEvent;//声明事件
// 烧水
public void BoilWater(){ for(int i = 0;i <= 100;i++){ temperature = i;
if(temperature > 95){ if(BoilEvent!= null){ //如果有对象注册
BoilEvent(temperature);//调用所有注册对象的方法 } } } } }
// 警报器
public class Alarm { public void MakeAlert(int param){ Console.WriteLine(“Alarm:嘀嘀嘀,水已经 {0} 度了:”, param);} }
// 显示器
public class Display { public static void ShowMsg(int param){ //静态方法
Console.WriteLine(“Display:水快烧开了,当前温度:{0}度。”, param);} }
class Program { static void Main(){ Heater heater = new Heater();Alarm alarm = new Alarm();
heater.BoilEvent += alarm.MakeAlert;//注册方法
heater.BoilEvent +=(new Alarm()).MakeAlert;//给匿名对象注册方法 heater.BoilEvent += Display.ShowMsg;//注册静态方法
heater.BoilWater();//烧水,会自动调用注册过对象的方法 } } }
输出为:
Alarm:嘀嘀嘀,水已经 96 度了: Alarm:嘀嘀嘀,水已经 96 度了: Display:水快烧开了,当前温度:96度。// 省略....Net Framework中的委托与事件
尽管上面的范例很好地完成了我们想要完成的工作,但是我们不仅疑惑:为什么.Net Framework 中的事件模型和上面的不同?为什么有很多的EventArgs参数?
在回答上面的问题之前,我们先搞懂.Net Framework的编码规范:
1.委托类型的名称都应该以EventHandler结束。
2.委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object 类型,一个 EventArgs类型(或继承自EventArgs)。
3.事件的命名为 委托去掉 EventHandler之后剩余的部分。4.继承自EventArgs的类型应该以EventArgs结尾。
再做一下说明:
1.委托声明原型中的Object类型的参数代表了Subject,也就是监视对象,在本例中是 Heater(热水器)。回调函数(比如Alarm的MakeAlert)可以通过它访问触发事件的对象(Heater)。
2.EventArgs 对象包含了Observer所感兴趣的数据,在本例中是temperature。
上面这些其实不仅仅是为了编码规范而已,这样也使得程序有更大的灵活性。比如说,如果我们不光想获得热水器的温度,还想在Observer端(警报器或者显示器)方法中获得它的生产日期、型号、价格,那么委托和方法的声明都会变得很麻烦,而如果我们将热水器的引用传给警报器的方法,就可以在方法中直接访问热水器了。
现在我们改写之前的范例,让它符合.Net Framework 的规范: using System;using System.Collections.Generic;using System.Text;
namespace Delegate { // 热水器
public class Heater { private int temperature;public string type = “RealFire 001”;// 添加型号作为演示 public string area = “China Xian”;// 添加产地作为演示
//声明委托
public delegate void BoiledEventHandler(Object sender, BoliedEventArgs e);public event BoiledEventHandler Boiled;//声明事件
// 定义BoliedEventArgs类,传递给Observer所感兴趣的信息 public class BoliedEventArgs : EventArgs { public readonly int temperature;public BoliedEventArgs(int temperature){ this.temperature = temperature;} }
// 可以供继承自 Heater 的类重写,以便继承类拒绝其他对象对它的监视 protected virtual void OnBolied(BoliedEventArgs e){ if(Boiled!= null){ // 如果有对象注册
Boiled(this, e);// 调用所有注册对象的方法 } }
// 烧水。
public void BoilWater(){ for(int i = 0;i <= 100;i++){ temperature = i;if(temperature > 95){ //建立BoliedEventArgs 对象。
BoliedEventArgs e = new BoliedEventArgs(temperature);OnBolied(e);// 调用 OnBolied方法 } } } }
// 警报器
public class Alarm { public void MakeAlert(Object sender, Heater.BoliedEventArgs e){ Heater heater =(Heater)sender;//这里是不是很熟悉呢? //访问 sender 中的公共字段
Console.WriteLine(“Alarm:{0}{1}: ”, heater.area, heater.type);Console.WriteLine(“Display:水快烧开了,当前温度:{0}度。”, e.temperature);Console.WriteLine();} }
class Program { static void Main(){ Heater heater = new Heater();Alarm alarm = new Alarm();
heater.Boiled += alarm.MakeAlert;//注册方法
heater.Boiled +=(new Alarm()).MakeAlert;//给匿名对象注册方法
heater.Boiled += new Heater.BoiledEventHandler(alarm.MakeAlert);//也可以这么注册 heater.Boiled += Display.ShowMsg;//注册静态方法
heater.BoilWater();//烧水,会自动调用注册过对象的方法 } } }
输出为:
Alarm:China XianRealFire 001: Alarm: 嘀嘀嘀,水已经 96 度了: Alarm:China XianRealFire 001: Display:水快烧开了,当前温度:96度。// 省略...总结
在本文中我首先通过一个GreetingPeople的小程序向大家介绍了委托的概念、委托用来做什么,随后又引出了事件,接着对委托与事件所产生的中间代码做了粗略的讲述。
在第二个稍微复杂点的热水器的范例中,我向大家简要介绍了 Observer设计模式,并通过实现这个范例完成了该模式,随后讲述了.Net Framework中委托、事件的实现方式。
本文的源码可以在http://www.tracefact.net/sourcecode/delegates-and-events.rar 下载。