版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、利用C#語言,調(diào)用存儲過程,使用三層架構(gòu)實現(xiàn)2009年3月13日利用C#語言,調(diào)用存儲過程,使用三層架構(gòu)實現(xiàn),我需要實現(xiàn)的功能見問題補充問題為:我需要實現(xiàn)的功能是:調(diào)用存儲過程,實現(xiàn)將數(shù)據(jù)庫中表的信息,顯示到頁面中,請幫忙寫出DAL層、BLL層、頁面顯示層,它們的代碼?謝謝!答案:先寫Dal層public datatable GetTable()SqlConnection conn=new SqlConnection ();conn.ConnectionString=Server=(local);database=db;uid=sa;pwd=sa;/連接數(shù)據(jù)源conn.Open();SqlCo
2、mmand cmd=new SqlCommand(Select,conn);/其中select為存儲過程名稱cmd.CommandType = CommandType.StoredProcedure;/指定執(zhí)行類型為存儲過程DataTable dt = new DataTable();/執(zhí)行存儲過程SqlDataAdapter sda=new SqlDataAdapter(cmd);/將結(jié)果填充到datatable中sda.Fill(dt);return dt;/返回DatatableBll層/實例化Dal層方法DAL dal=new DAL();public datatable BllGet
3、Table()/ 獲取數(shù)據(jù)表return dal.GetTable();頁面層/實例化Bll層方法Bll bll=new Bll();Datatable dt=new datatable();/ 調(diào)用Bll層方法dt=bll.BllGetTable();趣談三層架構(gòu)的演變(轉(zhuǎn)有關(guān)三層架構(gòu)有很多理解。下面用養(yǎng)豬為比喻,介紹三層架構(gòu)究竟是個什么東西。層次結(jié)構(gòu)在現(xiàn)實社會里隨處可見。記得有個笑話講有個村長得意地向他老婆吹牛:“全中國只有四個人比我官大,鄉(xiāng)長、縣長、省長和國務(wù)院總理”。這個笑話也體現(xiàn)了真實社會中分層的現(xiàn)象。社會人群會分層,公司人員結(jié)構(gòu)也會分層,樓房是分層的,甚至做包子的籠屜都是分層的。雖
4、然分層的目的各有不同,但都是為解決某一問題而產(chǎn)生的。所以,分層架構(gòu)其實是為了解決某一問題而產(chǎn)生的一種解決方案。14.1.1常用的三層架構(gòu)設(shè)計軟件系統(tǒng)最常用的一般會講到三層架構(gòu),其實就是將整個業(yè)務(wù)應(yīng)用劃分為表示層、業(yè)務(wù)邏輯層、數(shù)據(jù)訪問層等,有的還要細一些,通過分解業(yè)務(wù)細節(jié),將不同的功能代碼分散開來,更利于系統(tǒng)的設(shè)計和開發(fā),同時為可能的變更提供了更小的單元,十分有利于系統(tǒng)的維護和擴展。常見的三層架構(gòu)基本包括如下幾個部分,如圖 14-1 所示。圖 14- 1常見的三層架構(gòu)數(shù)據(jù)訪問層 DAL :用于實現(xiàn)與數(shù)據(jù)庫的交互和訪問,從數(shù)據(jù)庫獲取數(shù)據(jù)或保存數(shù)據(jù)到數(shù)據(jù)庫的部分。業(yè)務(wù)邏輯層 BLL :業(yè)務(wù)邏輯層承上
5、啟下,用于對上下交互的數(shù)據(jù)進行邏輯處理,實現(xiàn)業(yè)務(wù)目標。表示層 Web :主要實現(xiàn)和用戶的交互,接收用戶請求或返回用戶請求的數(shù)據(jù)結(jié)果的展現(xiàn),而具體的數(shù)據(jù)處理則交給業(yè)務(wù)邏輯層和數(shù)據(jù)訪問層去處理。日常開發(fā)的很多情況下為了復(fù)用一些共同的東西,會把一些各層都用的東西抽象出來。如我們將數(shù)據(jù)對象實體和方法分離,以便在多個層中傳遞, 例如稱為 Model 。一些共性的通用輔助類和工具方法,如數(shù)據(jù)校驗、緩存處理、加解密處理等,為了讓各個層之間復(fù)用,也單獨分離出來,作為獨立的模塊使用,例如稱為 Common 。此時,三層架構(gòu)會演變?yōu)槿鐖D 14-2 所示的情況。圖 14- 2三層架構(gòu)演變結(jié)果業(yè)務(wù)實體 Model :
6、 用于封裝實體類數(shù)據(jù)結(jié)構(gòu),一般用于映射數(shù)據(jù)庫的數(shù)據(jù)表或視圖,用以描述業(yè)務(wù)中客觀存在的對象。 Model 分離出來是為了更好地解耦,為了更好地發(fā)揮分層的作用,更好地進行復(fù)用和擴展,增強靈活性。通用類庫 Common :通用的輔助工具類。在第 5.2 節(jié)中我們講過可以將對數(shù)據(jù)庫的共性操作抽象封裝成數(shù)據(jù)操作類(例如 DbHelperSQL ),以便更好地復(fù)用和使代碼簡潔。數(shù)據(jù)層底層使用通用數(shù)據(jù)庫操作類來訪問數(shù)據(jù)庫,最后完整的三層架構(gòu)如圖 14-3 所示。圖 14- 3最后完整的三層架構(gòu)數(shù)據(jù)庫訪問類 是對 ADO.NET 的封裝,封裝了一些常用的重復(fù)的數(shù)據(jù)庫操作。如微軟的企業(yè)庫 SQLHelper.c
7、s ,動軟的 DBUtility/DbHelperSQL 等,為 DAL 提供訪問數(shù)據(jù)庫的輔助工具類。通過以上分析,我們知道如今常用的三層架構(gòu)是個什么樣子,同時,我們也知道了三層架構(gòu)在使用過程中的一些演化過程。那么,為什么要這樣分層,每層結(jié)構(gòu)到底又起什么作用呢?我們繼續(xù)往下看。14.1.2趣味理解:三層架構(gòu)與養(yǎng)豬看新聞報道今年豬肉價格一路高漲,據(jù)說有人養(yǎng)豬都發(fā)財致富奔小康了,程序員都說寫代碼沒前途了,還不如去養(yǎng)豬,不過,可別認為養(yǎng)豬沒有技術(shù)含量,比寫代碼容易,其實養(yǎng)豬也大有學問。為了更好地理解三層架構(gòu),就拿養(yǎng)豬來做個例子吧。俗話說:“沒吃過豬肉,還沒見過豬跑??!”。圖 14-4 是三層架構(gòu)化的
8、養(yǎng)豬產(chǎn)業(yè)流水線趣味對此圖。圖 14-4三層結(jié)構(gòu)與養(yǎng)豬對比圖 14-3 與圖 14- 4 ,我們可以看出:數(shù)據(jù)庫好比豬圈 ,所有的豬有序地按區(qū)域或編號,存放在不同的豬欄里。DAL 好比是屠宰場 ,把豬從豬圈取出來進行(處理)屠殺,按要求取出相應(yīng)的部位(字段),或者進行歸類整理(統(tǒng)計),形成整箱的豬肉(數(shù)據(jù)集),傳送給食品加工廠( BLL )。本來這里都是同一伙人既管抓豬,又管殺豬的,后來覺得效率太低了,就讓一部分人出來專管抓豬了( DBUtility ),根據(jù)要求來抓取指定的豬。BLL 好比食品加工廠 ,將豬肉深加工成各種可以食用的食品(業(yè)務(wù)處理)。Web 好比商場 ,將食品包裝成漂亮的可以銷售
9、的產(chǎn)品,展現(xiàn)給顧客( UI 表現(xiàn)層)。豬肉好比 Model ,無論是哪個廠(層),各個環(huán)節(jié)傳遞的本質(zhì)都是豬肉,豬肉貫穿整個過程。通用類庫 Common 相當于工人使用的各種工具,為各個廠(層)提供諸如殺豬刀、繩子、剪刀、包裝箱、工具車等共用的常用工具(類)。其實,每個部門本來是可以自己制作自己的工具的,但是那樣會使效率比較低,而且也不專業(yè),并且很多工作都會是重復(fù)的。因此,就專門有人開了這樣的工廠來制作這些工具,提供給各個工廠,有了這樣的分工,工廠就可以專心做自己的事情了。當然,這里只是形象的比喻,目的是為了讓大家更好地理解,實際的情況在細節(jié)上會有所不同。這個例子也只是說明了從豬圈到商場的單向過
10、程,而實際三層開發(fā)中的數(shù)據(jù)交互是雙向的,可取可存。不過,據(jù)說有一種機器,把豬從這頭趕進去,另一頭就噗噗嚕嚕地出火腿腸了。如果火腿腸賣不了了,從那頭再放進去,這頭豬又原原本本出來了,科幻的機器吧,沒想到也可以和三層結(jié)構(gòu)聯(lián)系上。以上只是笑談,不過也使三層架構(gòu)的基本概念更容易理解了。上面談了那么多,有人會問,我直接從數(shù)據(jù)庫取出內(nèi)容直接操作不可以嗎?為什么要這么麻煩地用三層架構(gòu)呢?三層架構(gòu)到底有什么好處呢?不分層,當然可以,就好比整個過程不分屠宰場、加工場之類的,都在同一個場所(工廠)完成所有的活(屠殺、加工、銷售)。但為什么要加工廠和商場呢?因為當規(guī)模比較大的時候,管理起來就會變得非常復(fù)雜,這樣的養(yǎng)
11、殖方式已經(jīng)無法滿足規(guī)模化的需要了。并且,從社會的發(fā)展來看,社會分工是人類進步的表現(xiàn) 。社會分工的優(yōu)勢就是讓適合的人做自己擅長的事情,使平均社會勞動時間大大縮短,生產(chǎn)效率顯著提高。能夠提供優(yōu)質(zhì)高效勞動產(chǎn)品的人才能在市場競爭中獲得高利潤和高價值。人盡其才,物盡其用最深刻的含義就是由社會分工得出的。軟件開發(fā)也一樣,做小項目的時候,分不分層確實看不出什么差別,并且顯得更麻煩啰嗦了。但當項目變大和變復(fù)雜時,分層就顯示出它的優(yōu)勢來了。所以分不分層要根據(jù)項目的實際情況而定,不能一概而論。三層架構(gòu)(2010-04-22 09:35:19) HYPERLINK javascript:; 轉(zhuǎn)載標簽: HYPERL
12、INK /c.php?t=blog&k=%C8%FD%B2%E3%BC%DC%B9%B9&ts=bpost&stype=tag t _blank 三層架構(gòu) HYPERLINK /c.php?t=blog&k=it&ts=bpost&stype=tag t _blank it分類: HYPERLINK /s/articlelist_1717044161_1_1.html t _blank C#三層架構(gòu)(3-tier application) 通常意義上的三層架構(gòu)就是將整個業(yè)務(wù)應(yīng)用劃分為:表現(xiàn)層(UI)、業(yè)務(wù)邏輯層(BLL)、數(shù)據(jù)訪問層(DAL)。區(qū)分層次的目的即為了“高內(nèi)聚,低耦合”的思想。、表
13、現(xiàn)層(UI):通俗講就是展現(xiàn)給用戶的界面,即用戶在使用一個系統(tǒng)的時候他的所見所得。、業(yè)務(wù)邏輯層(BLL):針對具體問題的操作,也可以說是對數(shù)據(jù)層的操作,對數(shù)據(jù)業(yè)務(wù)邏輯處理。、數(shù)據(jù)訪問層(DAL):該層所做事務(wù)直接操作數(shù)據(jù)庫,針對數(shù)據(jù)的增添、刪除、修改、更新、查找等。在軟件體系架構(gòu)設(shè)計中,分層式結(jié)構(gòu)是最常見,也是最重要的一種結(jié)構(gòu)。微軟推薦的分層式結(jié)構(gòu)一般分為三層,從下至上分別為:數(shù)據(jù)訪問層、業(yè)務(wù)邏輯層(又或成為領(lǐng)域?qū)樱⒈硎緦?。三層結(jié)構(gòu)原理:3個層次中,系統(tǒng)主要功能和業(yè)務(wù)邏輯都在業(yè)務(wù)邏輯層進行處理。所謂三層體系結(jié)構(gòu),是在客戶端與數(shù)據(jù)庫之間加入了一個“中間層”,也叫組件層。這里所說的三層體系,不是
14、指物理上的三層,不是簡單地放置三臺機器就是三層體系結(jié)構(gòu),也不僅僅有B/S應(yīng)用才是三層體系結(jié)構(gòu),三層是指邏輯上的三層,即使這三個層放置到一臺機器上。三層體系的應(yīng)用程序?qū)I(yè)務(wù)規(guī)則、數(shù)據(jù)訪問、合法性校驗等工作放到了中間層進行處理。通常情況下,客戶端不直接與數(shù)據(jù)庫進行交互,而是通過COM/DCOM通訊與中間層建立連接,再經(jīng)由中間層與數(shù)據(jù)庫進行交互。表示層:位于最外層(最上層),離用戶最近。用于顯示數(shù)據(jù)和接收用戶輸入的數(shù)據(jù),為用戶提供一種交互式操作的界面。業(yè)務(wù)邏輯層:業(yè)務(wù)邏輯層(Business Logic Layer)無疑是系統(tǒng)架構(gòu)中體現(xiàn)核心價值的部分。它的關(guān)注點主要集中在業(yè)務(wù)規(guī)則的制定、業(yè)務(wù)流程的實
15、現(xiàn)等與業(yè)務(wù)需求有關(guān)的系統(tǒng)設(shè)計,也即是說它是與系統(tǒng)所應(yīng)對的領(lǐng)域(Domain)邏輯有關(guān),很多時候,也將業(yè)務(wù)邏輯層稱為領(lǐng)域?qū)?。例如Martin Fowler在Patterns of Enterprise Application Architecture一書中,將整個架構(gòu)分為三個主要的層:表示層、領(lǐng)域?qū)雍蛿?shù)據(jù)源層。作為領(lǐng)域驅(qū)動設(shè)計的先驅(qū)Eric Evans,對業(yè)務(wù)邏輯層作了更細致地劃分,細分為應(yīng)用層與領(lǐng)域?qū)?,通過分層進一步將領(lǐng)域邏輯與領(lǐng)域邏輯的解決方案分離。業(yè)務(wù)邏輯層在體系架構(gòu)中的位置很關(guān)鍵,它處于數(shù)據(jù)訪問層與表示層中間,起到了數(shù)據(jù)交換中承上啟下的作用。由于層是一種弱耦合結(jié)構(gòu),層與層之間的依賴是向下
16、的,底層對于上層而言是“無知”的,改變上層的設(shè)計對于其調(diào)用的底層而言沒有任何影響。如果在分層設(shè)計時,遵循了面向接口設(shè)計的思想,那么這種向下的依賴也應(yīng)該是一種弱依賴關(guān)系。因而在不改變接口定義的前提下,理想的分層式架構(gòu),應(yīng)該是一個支持可抽取、可替換的“抽屜”式架構(gòu)。正因為如此,業(yè)務(wù)邏輯層的設(shè)計對于一個支持可擴展的架構(gòu)尤為關(guān)鍵,因為它扮演了兩個不同的角色。對于數(shù)據(jù)訪問層而言,它是調(diào)用者;對于表示層而言,它卻是被調(diào)用者。依賴與被依賴的關(guān)系都糾結(jié)在業(yè)務(wù)邏輯層上,如何實現(xiàn)依賴關(guān)系的解耦,則是除了實現(xiàn)業(yè)務(wù)邏輯之外留給設(shè)計師的任務(wù)。數(shù)據(jù)層:數(shù)據(jù)訪問層:有時候也稱為是持久層,其功能主要是負責數(shù)據(jù)庫的訪問,可以訪
17、問數(shù)據(jù)庫系統(tǒng)、二進制文件、文本文檔或是XML文檔。簡單的說法就是實現(xiàn)對數(shù)據(jù)表的Select,Insert,Update,Delete的操作。如果要加入ORM的元素,那么就會包括對象和數(shù)據(jù)表之間的mapping,以及對象實體的持久化。優(yōu)點:1、開發(fā)人員可以只關(guān)注整個結(jié)構(gòu)中的其中某一層;2、可以很容易的用新的實現(xiàn)來替換原有層次的實現(xiàn);3、可以降低層與層之間的依賴;4、有利于標準化;5、利于各層邏輯的復(fù)用。缺點:1、降低了系統(tǒng)的性能。這是不言而喻的。如果不采用分層式結(jié)構(gòu),很多業(yè)務(wù)可以直接造訪數(shù)據(jù)庫,以此獲取相應(yīng)的數(shù)據(jù),如今卻必須通過中間層來完成。2、有時會導致級聯(lián)的修改。這種修改尤其體現(xiàn)在自上而下的
18、方向。如果在表示層中需要增加一個功能,為保證其設(shè)計符合分層式結(jié)構(gòu),可能需要在相應(yīng)的業(yè)務(wù)邏輯層和數(shù)據(jù)訪問層中都增加相應(yīng)的代碼。規(guī)則:三層結(jié)構(gòu)的程序不是說把項目分成DAL, BLL, WebUI三個模塊就叫三層了, 下面幾個問題在你的項目面:1. UILayer里面只有少量(或者沒有)的SQL語句或者存儲過程調(diào)用, 并且這些語句保證不會修改數(shù)據(jù)?2. 如果把UILayer拿掉, 你的項目還能在Interface/API的層次上提供所有功能嗎?3. 你的DAL可以移植到其他類似環(huán)境的項目嗎?4. 三個模塊, 可以分別運行于不同的服務(wù)器嗎?如果不是所有答案都為YES, 那么你的項目還不能算是嚴格意義上
19、的三層程序. 三層程序有一些需要約定遵守的規(guī)則:1. 最關(guān)鍵的, UI層只能作為一個外殼, 不能包含任何BizLogic的處理過程.2. 設(shè)計時應(yīng)該從BLL出發(fā), 而不是UI出發(fā). BLL層在API上應(yīng)該實現(xiàn)所有BizLogic, 以面向?qū)ο蟮姆绞?3. 不管數(shù)據(jù)層是一個簡單的SqlHelper也好, 還是帶有Mapping過的Classes也好, 應(yīng)該在一定的抽象程度上做到系統(tǒng)無關(guān).4. 不管使用COM+(Enterprise Service), 還是Remoting, 還是WebService之類的遠程對象技術(shù), 不管部署的時候是不是真的分別部署到不同的服務(wù)器上, 最起碼在設(shè)計的時候要做這
20、樣的考慮, 更遠的, 還得考慮多臺服務(wù)器通過負載均衡作集群.所以考慮一個項目是不是應(yīng)該應(yīng)用三層/多層設(shè)計時, 先得考慮下是不是真的需要? 實際上大部分程序就開個WebApplication就足夠了, 完全沒必要作的這么復(fù)雜. 而多層結(jié)構(gòu), 是用于解決真正復(fù)雜的項目需求的。C#委托和事件例析 (2010-04-08 14:18:51) HYPERLINK javascript:; 轉(zhuǎn)載標簽: 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在語法上非常相似,而最初讓我比較困惑的就是委托、事件部分,相信大多數(shù)初學者也有類似的困惑。經(jīng)過跟Java的對比學習,發(fā)現(xiàn)這其實跟Java的監(jiān)聽、事件是等同的,只是表述上不同罷了。委托+事件是觀察者模式的一個典型例子,所謂的委托其實就是觀察者,它會關(guān)心某種事件,一旦這種事件被觸發(fā),這個觀察者就會行動。下面是最近寫的一個例子,相信能夠加深大家對委托和事件的理解。using System;using System.Collections.Generic;using System.
22、Text;namespace ConsoleApplication3public delegate void TimeEventHandler(object obj, TimeEventArgsargs); /定義一個委托,委托其實就是“方法模板”,就好像“類”是“對象”的模板一樣。如果某個類想在事件觸發(fā)的時候收到通知,它必須有一個符合這種格式的方法,在這個例子中,就是:返回類型為void,參數(shù)類型為object、TimeEventArgs。/TimeEventArgs是我們自己定義的一個類,用于保存事件中的參數(shù)。這里我們分別保存時間的時分秒。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),從這個方法的定義可以看到,我們只會關(guān)心返回類型和方法的參數(shù),而方法名稱則無所謂。class MyTimeEventHandlerClasspublic void ShowTime(object obj, TimeEventArgs args)Console.WriteLine(現(xiàn)在的時間是:+args.Hour+:+args.Minute+:+args.Second);/時鐘類class Clock/我們在這個類中定義了一個“TimeChanged”事件,注意其前面有兩個關(guān)鍵字“
25、event”和“TimeEventHandler”,其中event表示這是一個事件,而不是方法或?qū)傩?;TimeEventHandler則指出,誰要監(jiān)聽TimeChanged事件,它就必須有一個符合TimeEventHandler(委托)的方法。public event TimeEventHandler TimeChanged;public Clock()TimeChanged = null; /注意,這里的null的含義是指TimeChanged事件當前還沒有觀察者關(guān)注它,如果某個觀察者要關(guān)注TimeChanged事件,它必須要讓這個事件知道,方法是使用操作符“+=”來借助委托將其加載到事件上
26、。/時鐘開始走動,我們的目標是每秒鐘觸發(fā)一次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、來保存相關(guān)參數(shù),這里是時分秒。TimeEventArgs args = new TimeEventArgs(h2,m2, s2);/注意這種寫法,這一句是用來觸發(fā)事件,事件不是類,所以不用使用“new”關(guān)鍵字,而且我們看到,這里TimeChanged的兩個參數(shù)跟我們的委托(TimeEventHandler)是一致的,其中第一個參數(shù)是觸發(fā)這個事件的對象,我們這里使用的是一個時鐘實例(this)。TimeChanged(this, args);class Programstatic void Main(string args)Clock clock = new Clock(); /實例化一個時鐘M
28、yTimeEventHandlerClass tehc = new MyTimeEventHandlerClass(); /實例化一個觀察者類/將事件跟我們定義的觀察者進行連接,這樣,clock就會知道,每當TimeChanged事件被觸發(fā),就會去通知這個觀察者,注意我們連接的時候使用的并不是直接的觀察者類實例中的ShowTime()方法,而是一個委托,并在這個委托中傳遞ShowTime()方法,這也是“委托”的真正意義所在我有一個方法,但我委托你來幫我關(guān)聯(lián)到事件,因為事件只會直接跟委托打交道,而不是觀察者的具體某個方法。clock.TimeChanged+=new TimeEventHand
29、ler(tehc.ShowTime);clock.go();C#中的委托和事件B(精) (2010-04-08 11:24:29) HYPERLINK javascript:; 轉(zhuǎn)載標簽: 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的聲明語句做一探究,看看為什么會發(fā)生這樣的錯誤:publice
30、ventGreetingDelegateMakeGreet;可以看到,實際上盡管我們在GreetingManager里將 MakeGreet 聲明為public,但是,實際上MakeGreet會被編譯成私有字段,難怪會發(fā)生上面的編譯錯誤了,因為它根本就不允許在GreetingManager類的外面以賦值的方式訪問,從而驗證了我們上面所做的推論。我們再進一步看下MakeGreet所產(chǎn)生的代碼: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);現(xiàn)在已經(jīng)很明確了:MakeGree
32、t事件確實是一個GreetingDelegate類型的委托,只不過不管是不是聲明為public,它總是被聲明為private。另外,它還有兩個方法,分別是add_MakeGreet和remove_MakeGreet,這兩個方法分別用于注冊委托類型的方法和取消注冊。實際上也就是: “+= ”對應(yīng) add_MakeGreet,“-=”對應(yīng)remove_MakeGreet。而這兩個方法的訪問限制取決于聲明事件時的訪問限制符。在add_MakeGreet()方法內(nèi)部,實際上調(diào)用了System.Delegate的Combine()靜態(tài)方法,這個方法用于將當前的變量添加到委托鏈表中。我們前面提到過兩次,說
33、委托實際上是一個類,在我們定義委托的時候:publicdelegatevoidGreetingDelegate(stringname);當編譯器遇到這段代碼的時候,會生成下面這樣一個完整的類:publicsealedclassGreetingDelegate:System.MulticastDelegatepublicGreetingDelegate(objectobject, IntPtr method);publicvirtualIAsyncResult BeginInvoke(stringname, AsyncCallback callback,objectobject);publicv
34、irtualvoidEndInvoke(IAsyncResult result);publicvirtualvoidInvoke(stringname);關(guān)于這個類的更深入內(nèi)容,可以參閱CLR Via C#等相關(guān)書籍,這里就不再討論了。委托、事件與Observer設(shè)計模式范例說明上面的例子已不足以再進行下面的講解了,我們來看一個新的范例,因為之前已經(jīng)介紹了很多的內(nèi)容,所以本節(jié)的進度會稍微快一些:假設(shè)我們有個高檔的熱水器,我們給它通上電,當水溫超過95度的時候:1、揚聲器會開始發(fā)出語音,告訴你水的溫度;2、液晶屏也會改變水溫的顯示,來提示水已經(jīng)快燒開了。現(xiàn)在我們需要寫個程序來模擬這個燒水的過程,
35、我們將定義一個類來代表熱水器,我們管它叫:Heater,它有代表水溫的字段,叫做temperature;當然,還有必不可少的給水加熱方法BoilWater(),一個發(fā)出語音警報的方法MakeAlert(),一個顯示水溫的方法,ShowMsg()。namespaceDelegate classHeaterprivateinttemperature;/ 水溫/ 燒水publicvoidBoilWater() for(inti = 0; i 95) MakeAlert(temperature);ShowMsg(temperature);/ 發(fā)出語音警報privatevoidMakeAlert(int
36、param) Console.WriteLine(Alarm:嘀嘀嘀,水已經(jīng) 0 度了:, param);/ 顯示水溫privatevoidShowMsg(intparam) Console.WriteLine(Display:水快開了,當前溫度:0度。, param);classProgramstaticvoidMain() Heaterht =newHeater();ht.BoilWater();Observer設(shè)計模式簡介上面的例子顯然能完成我們之前描述的工作,但是卻并不夠好?,F(xiàn)在假設(shè)熱水器由三部分組成:熱水器、警報器、顯示器,它們來自于不同廠商并進行了組裝。那么,應(yīng)該是熱水器僅僅負責燒
37、水,它不能發(fā)出警報也不能顯示水溫;在水燒開時由警報器發(fā)出警報、顯示器顯示提示和水溫。這時候,上面的例子就應(yīng)該變成這個樣子:/ 熱水器publicclassHeaterprivateinttemperature;/ 燒水privatevoidBoilWater() for(inti = 0; i = 100; i+) temperature = i;/ 警報器publicclassAlarmprivatevoidMakeAlert(intparam) Console.WriteLine(Alarm:嘀嘀嘀,水已經(jīng) 0 度了:, param);/ 顯示器publicclassDisplaypriv
38、atevoidShowMsg(intparam) Console.WriteLine(Display:水已燒開,當前溫度:0度。, param);這里就出現(xiàn)了一個問題:如何在水燒開的時候通知報警器和顯示器?在繼續(xù)進行之前,我們先了解一下Observer設(shè)計模式,Observer設(shè)計模式中主要包括如下兩類對象:Subject:監(jiān)視對象,它往往包含著其他對象所感興趣的內(nèi)容。在本范例中,熱水器就是一個監(jiān)視對象,它包含的其他對象所感興趣的內(nèi)容,就是temprature字段,當這個字段的值快到100時,會不斷把數(shù)據(jù)發(fā)給監(jiān)視它的對象。Observer:監(jiān)視者,它監(jiān)視Subject,當Subject中的某件
39、事發(fā)生的時候,會告知Observer,而Observer則會采取相應(yīng)的行動。在本范例中,Observer有警報器和顯示器,它們采取的行動分別是發(fā)出警報和顯示水溫。在本例中,事情發(fā)生的順序應(yīng)該是這樣的:警報器和顯示器告訴熱水器,它對它的溫度比較感興趣(注冊)。熱水器知道后保留對警報器和顯示器的引用。熱水器進行燒水這一動作,當水溫超過95度時,通過對警報器和顯示器的引用,自動調(diào)用警報器的MakeAlert()方法、顯示器的ShowMsg()方法。類似這樣的例子是很多的,GOF對它進行了抽象,稱為Observer設(shè)計模式:Observer設(shè)計模式是為了定義對象間的一種一對多的依賴關(guān)系,以便于當一個對
40、象的狀態(tài)改變時,其他依賴于它的對象會被自動告知并更新。Observer模式是一種松耦合的設(shè)計模式。實現(xiàn)范例的Observer設(shè)計模式我們之前已經(jīng)對委托和事件介紹很多了,現(xiàn)在寫代碼應(yīng)該很容易了,現(xiàn)在在這里直接給出代碼,并在注釋中加以說明。usingSystem;usingSystem.Collections.Generic;usingSystem.Text;namespaceDelegate / 熱水器publicclassHeaterprivateinttemperature;publicdelegatevoidBoilHandler(intparam);/聲明委托publiceventBoi
41、lHandlerBoilEvent;/聲明事件/ 燒水publicvoidBoilWater() for(inti = 0; i 95) if(BoilEvent !=null) /如果有對象注冊BoilEvent(temperature);/調(diào)用所有注冊對象的方法/ 警報器publicclassAlarmpublicvoidMakeAlert(intparam) Console.WriteLine(Alarm:嘀嘀嘀,水已經(jīng) 0 度了:, param);/ 顯示器publicclassDisplaypublicstaticvoidShowMsg(intparam) /靜態(tài)方法Console.
42、WriteLine(Display:水快燒開了,當前溫度:0度。, param);classProgramstaticvoidMain() Heaterheater =newHeater();Alarmalarm =newAlarm();heater.BoilEvent += alarm.MakeAlert;/注冊方法heater.BoilEvent += (newAlarm().MakeAlert;/給匿名對象注冊方法heater.BoilEvent += Display.ShowMsg;/注冊靜態(tài)方法heater.BoilWater();/燒水,會自動調(diào)用注冊過對象的方法輸出為:Alarm
43、:嘀嘀嘀,水已經(jīng) 96 度了:Alarm:嘀嘀嘀,水已經(jīng) 96 度了:Display:水快燒開了,當前溫度:96度。/ 省略.Net Framework中的委托與事件盡管上面的范例很好地完成了我們想要完成的工作,但是我們不僅疑惑:為什么.Net Framework 中的事件模型和上面的不同?為什么有很多的EventArgs參數(shù)?在回答上面的問題之前,我們先搞懂 .Net Framework的編碼規(guī)范:委托類型的名稱都應(yīng)該以EventHandler結(jié)束。委托的原型定義:有一個void返回值,并接受兩個輸入?yún)?shù):一個Object 類型,一個 EventArgs類型(或繼承自EventArgs)。事
44、件的命名為 委托去掉 EventHandler之后剩余的部分。繼承自EventArgs的類型應(yīng)該以EventArgs結(jié)尾。再做一下說明:委托聲明原型中的Object類型的參數(shù)代表了Subject,也就是監(jiān)視對象,在本例中是 Heater(熱水器)?;卣{(diào)函數(shù)(比如Alarm的MakeAlert)可以通過它訪問觸發(fā)事件的對象(Heater)。EventArgs 對象包含了Observer所感興趣的數(shù)據(jù),在本例中是temperature。上面這些其實不僅僅是為了編碼規(guī)范而已,這樣也使得程序有更大的靈活性。比如說,如果我們不光想獲得熱水器的溫度,還想在Observer端(警報器或者顯示器)方法中獲得它
45、的生產(chǎn)日期、型號、價格,那么委托和方法的聲明都會變得很麻煩,而如果我們將熱水器的引用傳給警報器的方法,就可以在方法中直接訪問熱水器了?,F(xiàn)在我們改寫之前的范例,讓它符合 .Net Framework 的規(guī)范:usingSystem;usingSystem.Collections.Generic;usingSystem.Text;namespaceDelegate / 熱水器publicclassHeaterprivateinttemperature;publicstringtype =RealFire 001;/ 添加型號作為演示publicstringarea =China Xian;/ 添加
46、產(chǎn)地作為演示/聲明委托publicdelegatevoidBoiledEventHandler(Object sender, BoiledEventArgs e);publiceventBoiledEventHandlerBoiled;/聲明事件/ 定義BoiledEventArgs類,傳遞給Observer所感興趣的信息publicclassBoiledEventArgs:EventArgspublicreadonlyinttemperature;publicBoiledEventArgs(inttemperature) this.temperature = temperature;/ 可以
47、供繼承自 Heater 的類重寫,以便繼承類拒絕其他對象對它的監(jiān)視protectedvirtualvoidOnBoiled(BoiledEventArgs e) if(Boiled !=null) / 如果有對象注冊Boiled(this, e);/ 調(diào)用所有注冊對象的方法/ 燒水。publicvoidBoilWater() for(inti = 0; i 95) /建立BoiledEventArgs 對象。BoiledEventArgs e =newBoiledEventArgs(temperature);OnBoiled(e);/ 調(diào)用 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: 嘀嘀嘀,水已經(jīng) 0 度了:, e.temperature);Console.WriteLine();/ 顯示器publicclassDisplaypublicstaticvoidSho
49、wMsg(Object sender, Heater.BoiledEventArgs e) /靜態(tài)方法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;/注冊靜態(tài)方法heater.BoilWater();/燒水,會自動調(diào)用注冊過對象的方法輸出為:Alarm:China Xian - RealFire 001:Alarm: 嘀嘀嘀,水已經(jīng) 96 度了:Alarm:China X
51、ian - RealFire 001:Alarm: 嘀嘀嘀,水已經(jīng) 96 度了:Alarm:China Xian - RealFire 001:Alarm: 嘀嘀嘀,水已經(jīng) 96 度了:Display:China Xian - RealFire 001:Display:水快燒開了,當前溫度:96度。/ 省略.總結(jié)在本文中我首先通過一個GreetingPeople的小程序向大家介紹了委托的概念、委托用來做什么,隨后又引出了事件,接著對委托與事件所產(chǎn)生的中間代碼做了粗略的講述。在第二個稍微復(fù)雜點的熱水器的范例中,我向大家簡要介紹了 Observer設(shè)計模式,并通過實現(xiàn)這個范例完成了該模式,隨后講述
52、了.Net Framework中委托、事件的實現(xiàn)方式。希望這篇文章能給你帶來幫助。C#中的委托和事件A(精)(2010-04-08 11:12:36) HYPERLINK javascript:; 轉(zhuǎn)載標簽: 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中的應(yīng)用非常廣泛,然而,較好地理解委托和事件對很多接觸C#時間不長的人來說并不容易。它們就像是一道檻兒,過了這個檻的人,覺得真是太容易了,而沒有過去的人每次見到委托和事件就覺得心里別(bi)得慌,混身不自在。本文中,我將通過兩個范例由淺入深地講述什么是委托、為什么要使用委托、事件的由來
54、、.Net Framework中的委托和事件、委托和事件對Observer設(shè)計模式的意義,對它們的中間代碼也做了討論。將方法作為方法的參數(shù)我們先不管這個標題如何的繞口,也不管委托究竟是個什么東西,來看下面這兩個最簡單的方法,它們不過是在屏幕上輸出一句問候的話語:publicvoidGreetPeople(stringname) / 做某些額外的事情,比如初始化之類,此處略EnglishGreeting(name);publicvoidEnglishGreeting(stringname) Console.WriteLine(Morning, + name);暫且不管這兩個方法有沒有什么實際意義
55、。GreetPeople用于向某人問好,當我們傳遞代表某人姓名的name參數(shù),比如說“Jimmy”,進去的時候,在這個方法中,將調(diào)用EnglishGreeting方法,再次傳遞name參數(shù),EnglishGreeting則用于向屏幕輸出 “Morning, Jimmy”。現(xiàn)在假設(shè)這個程序需要進行全球化,哎呀,不好了,我是中國人,我不明白“Morning”是什么意思,怎么辦呢?好吧,我們再加個中文版的問候方法:publicvoidChineseGreeting(stringname)Console.WriteLine(早上好, + name);這時候,GreetPeople也需要改一改了,不然如
56、何判斷到底用哪個版本的Greeting問候方法合適呢?在進行這個之前,我們最好再定義一個枚舉作為判斷的依據(jù):publicenumLanguageEnglish, ChinesepublicvoidGreetPeople(stringname, Language lang)/做某些額外的事情,比如初始化之類,此處略swith(lang)caseLanguage.English:EnglishGreeting(name);break;caseLanguage.Chinese:ChineseGreeting(name);break;OK,盡管這樣解決了問題,但我不說大家也很容易想到,這個解決方案的可
57、擴展性很差,如果日后我們需要再添加韓文版、日文版,就不得不反復(fù)修改枚舉和GreetPeople()方法,以適應(yīng)新的需求。在考慮新的解決方案之前,我們先看看 GreetPeople的方法簽名:publicvoidGreetPeople(stringname, Language lang)我們僅看 string name,在這里,string 是參數(shù)類型,name 是參數(shù)變量,當我們賦給name字符串“jimmy”時,它就代表“jimmy”這個值;當我們賦給它“張子陽”時,它又代表著“張子陽”這個值。然后,我們可以在方法體內(nèi)對這個name進行其他操作。哎,這簡直是廢話么,剛學程序就知道了。如果你再
58、仔細想想,假如GreetPeople()方法可以接受一個參數(shù)變量,這個變量可以代表另一個方法,當我們給這個變量賦值 EnglishGreeting的時候,它代表著 EnglsihGreeting() 這個方法;當我們給它賦值ChineseGreeting 的時候,它又代表著ChineseGreeting()方法。我們將這個參數(shù)變量命名為 MakeGreeting,那么不是可以如同給name賦值時一樣,在調(diào)用 GreetPeople()方法的時候,給這個MakeGreeting 參數(shù)也賦上值么(ChineseGreeting或者EnglsihGreeting等)?然后,我們在方法體內(nèi),也可以像使
59、用別的參數(shù)一樣使用MakeGreeting。但是,由于MakeGreeting代表著一個方法,它的使用方式應(yīng)該和它被賦的方法(比如ChineseGreeting)是一樣的,比如:MakeGreeting(name);好了,有了思路了,我們現(xiàn)在就來改改GreetPeople()方法,那么它應(yīng)該是這個樣子了:publicvoidGreetPeople(stringname, * MakeGreeting)MakeGreeting(name);注意到 * ,這個位置通常放置的應(yīng)該是參數(shù)的類型,但到目前為止,我們僅僅是想到應(yīng)該有個可以代表方法的參數(shù),并按這個思路去改寫GreetPeople方法,現(xiàn)在就
60、出現(xiàn)了一個大問題:這個代表著方法的MakeGreeting參數(shù)應(yīng)該是什么類型的?NOTE:這里已不再需要枚舉了,因為在給MakeGreeting賦值的時候動態(tài)地決定使用哪個方法,是ChineseGreeting還是 EnglishGreeting,而在這個兩個方法內(nèi)部,已經(jīng)對使用“morning”還是“早上好”作了區(qū)分。聰明的你應(yīng)該已經(jīng)想到了,現(xiàn)在是委托該出場的時候了,但講述委托之前,我們再看看MakeGreeting參數(shù)所能代表的 ChineseGreeting()和EnglishGreeting()方法的簽名:publicvoidEnglishGreeting(stringname)pub
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024中外合資企業(yè)教育培訓與經(jīng)營合同書
- 2024廣州市房地產(chǎn)中介服務(wù)合同(賣方出租方使用)
- 2024個人民間借款合同范例
- 2024年信息安全保密協(xié)議
- 2024年合伙人分伙協(xié)議書
- 2024果樹苗木定購合同范本
- 跨境電商商品銷售合同
- 承包商土地使用權(quán)贈與合同模板
- 精裝修室內(nèi)工程合同
- 2024英文合同范本
- 湘少版英語三下《Unit6Whatcolouristhisballoon》PPT課件2[wwwedudownnet]
- 風景區(qū)改造工程施工組織設(shè)計(131頁)
- 【課件】甜甜的滋味雙頁
- 造林施工組織設(shè)計
- 常用偏旁部首(對外漢語)
- 國際消費者研究(共85頁).ppt
- 八級體育武術(shù)健身南拳教案圖文稿
- 三年級作文——觀察桔子-PPT課件(共24張)
- 第六章 氣體射流ppt課件
- 初三化學上冊第二單元知識點總結(jié)
- 二年級上冊100以內(nèi)加減乘法混合運算精選題
評論
0/150
提交評論