版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第五章面向?qū)ο缶幟嫦驅(qū)ο缶幊蹋∣bject-orientedprogramming)是第三種編程模式(paradigm)。有趨勢表明,函數(shù)(polymorphism)和繼承確定義的邊界之后,這樣,更易于管理程序的結(jié)構(gòu)。在F#,隱藏的辦法除了可以簡單地定義成本地函數(shù)的類型為int->int,它可以實(shí)現(xiàn)為加上給定參數(shù)的函數(shù),減少給定參數(shù)的函數(shù),或者一個(gè)百萬數(shù)列中的一項(xiàng)。還可以在已有的抽象組件之外構(gòu)建抽象實(shí)體,比如,定義在.NETBCL類型;接口繼承(interfaceinheritance)。比如,在.NETBCL中的集合類型就是分層次的,分布在System.Collections和System.Collections.Generic命名空間中。inheritance),它在F#編程中往往并不重要,因?yàn)楹瘮?shù)編程本身對定義和共享實(shí)現(xiàn)片段提供了的靈活性。然而,對于一些特定領(lǐng)域會(huì)很重要,比如,圖形用戶界面(graphicaluserinterface,String.Lengths),重寫成點(diǎn)符號(hào)(s.Length)一樣,這樣的簡化使代碼更好理解。在這一章,我們會(huì)看到如何在F#附加成員到類、類型上,只要需要,可以所有代碼按面向?qū)ο箫L(fēng)格重新組織。F#提供了豐富的面向?qū)ο缶幊棠P?,用來?chuàng)建類、接口和對象,與通過C#和VB.NET創(chuàng)建的行為非常相似。可能更為重要的是F創(chuàng)建的類,與使用其他語言創(chuàng)建的類,當(dāng)在一個(gè)庫函數(shù)中,從這如何以面向?qū)ο箫L(fēng)格使用F#本地類型編程。記錄作對象(RecordsAs函數(shù),可以模擬對象的方法。與F#的類相比,這項(xiàng)技術(shù)既有限制,也有優(yōu)勢。(sinture有的論。我們看一個(gè)簡單的例子,演示如何使用記錄作對象。它定義了Shape,有兩個(gè)成員:第一個(gè)是reposition,函數(shù)類型,移動(dòng)圖形;第二個(gè)draw,繪制圖形。makeShap建Sharp實(shí)例;makeShape函數(shù)實(shí)現(xiàn)重新定位功能,參數(shù)initPos保存在可變的(ref)單元中,調(diào)用函數(shù)reposition進(jìn)行更新。這就是說,圖形的位置被封裝,只能通過成員reposition,以這種方法隱藏值在F#編程中很常用。open//aShaperecordthatwillaourobjecttypeShape={Reposition:Point->unit;Draw:unit->unit}//createanewinstanceofletmakeShapeinitPosdraw//currPosistheinternalstateoftheobjectletcurrPos=refinitPos{Reposition//theRepositionmemberupdatestheinternalstate(funnewPos->currPos:=newPos);Draw//drawtheshapepassingthecurrent//togivendrawfunction(fun()->draw!currPos);}//"draws"ashape,printsouttheshapesnameandpositionletdrawshape(pos:Point)=printfn"%s,withx=%iandy=%i"shapepos.Xpos.Y//createsanewcircleshapeletcircleinitPos=makeShapeinitPos(draw//createsanewsquareshapeletsquareinitPos=makeShapeinitPos(draw//listofshapesintheirinitalpositionsletshapes=[circle(newPoint(10,10));square(newPoint(30,30))//drawalltheshapesletdrawShapes()=shapes|>List.iter(funs->letmain()drawShapes()//drawthe//movealltheshapes|>List.iter(funs->s.Reposition(newPoint(40,40)))drawShapes()//drawtheshapes//startthe運(yùn)行結(jié)果如下Circle,withx=10andy=10Square,withx=30andy=30Circle,withx=40andy=40Square,withx=40andy=openopenopen//aShaperecordthatwillaourobjecttypeShape={Reposition:Point->unit;Draw:Graphics->unit}//createanewinstanceofShapeletmovingShapeinitPosdraw=//currPosistheinternalstateoftheobjectletcurrPos=refinitPosin{Reposition//theRepositionmemberupdatestheinternalstate(funnewPos->currPos:=newPos);Draw//drawtheshapepassingthecurrent//andgraphicsobjecttogivendrawfunction(fung->draw!currPosg);}//createanewcircleShapeletmovingCircleinitPosdiam=movingShapeinitPos(funposg->//createanewsquareShapeletmovingSquareinitPossize=movingShapeinitPos(funposg->g.DrawRectangle(Pens.Blue,pos.X,pos.Y,size,size))//listofshapesintheirinitalpositionsletshapes=[movingCircle(newPoint(10,10))movingSquare(newPoint(30,30))movingCircle(newPoint(20,20))movingCircle(newPoint(40,40))20;//createtheformtoshowtheitemsletmainForm=letform=newForm()letrand=newRandom()//addaneventhandlertodrawtheshapesform.Paint.Add(fune->shapes|>List.iter(funs->s.Drawe.Graphics))//addaneventhandlertomovethe//whentheuserclickstheformform.Click.Add(fune->shapes|>List.iter(funs->s.Reposition(newPoint(rand.Next(form.Width),//Showtheformandstarttheeventloopdo程序產(chǎn)生一個(gè)圖形界面,如圖5-1圖5-1擬對象繪制圖再次,定義記錄類型Shape,它有兩個(gè)成員Reposition和Rraw;然后,定義函數(shù)makeCircle和makeSquare不同的圖形,并用它Shape型錄列表;最后,定義窗體處理這些記錄。這里,必須多做一些事情,因?yàn)闆]有使用繼承,BCL的System.Winows.Forms.Form根本不知道有關(guān)Sharp對象,因此,必須對這個(gè)列表進(jìn)行迭代,顯式繪制每一個(gè)圖形。實(shí)際上,也相當(dāng)簡單,只要三行代碼,添加一個(gè)事件處理程序到mainForm的Paint事件中。fune->List.iter(funs->s.drawe.Graphics)在下一節(jié)還將看到,以更加自然地方法表現(xiàn)操作:為F#類型添加成員。有成員的F#可以為F#記錄和聯(lián)合類型添加函數(shù)。調(diào)用添加到記錄或聯(lián)合類型的函數(shù)可以用點(diǎn)符號(hào),就像是調(diào)用非F#寫的庫函數(shù)中的類成員一樣;同時(shí),對于向其他.NET語言提供用F#定義的類型也是很有用對所有的F#類型也這樣做提供了一種更好的方法。定義F#或聯(lián)合類型的語法與第三章中學(xué)過的語法相同,只是這里要包含成員定義,總是放在最后,在關(guān)鍵字with的后面。成員本身的定義,用關(guān)鍵字member,加標(biāo)識(shí)符,表示成員附屬于該類型的參數(shù),加點(diǎn),加函數(shù)名,加函數(shù)需要的其他參數(shù);之后是等號(hào),加函數(shù)定義,可以是任意F#下面的例子定義一個(gè)記錄類型Point,有兩個(gè)字段Left和Top,一個(gè)成員函數(shù)Swap。函數(shù)Swap很簡單,用交換Left和Top之后的值產(chǎn)生一個(gè)新的點(diǎn)。注意如何使用參數(shù)x,放在函數(shù)名Swap之前, //ApointtypetypePoint={Top:int;Left:int}//theswapmembercreatesanew//withtheleft/topcoordsreveresedmemberx.Swap()={Top=Left=x.Top//createanewpointletmyPoint={Top=Left=7letmain()//printtheinitalpointprintfn"%A"myPoint//createanewpointwiththecoordsswappedletnextPoint=myPoint.Swap()//printthenewpointprintfn"%A"http://starttheapp示例運(yùn)行結(jié)果如{top=left={top=left=你可能已經(jīng)注意到,在函數(shù)Swap中的x:memberx.Swap()={Top=x.Left;Left=x.TopletnextPoint=問這個(gè)值的字段和方法。有些面向?qū)ο笳Z言使用專門的關(guān)鍵字,比如this或Me,但F#可以讓你選擇參數(shù)名,在關(guān)鍵字member后面,給它指定一個(gè)名字,比如這里的x。聯(lián)合類型也可以有成員函數(shù),定義的方法同記錄類型。下面的例子定義了一個(gè)聯(lián)合類型DrinkAmount,//atyperepresentingtheamountofaspecifictypeDrinkAmount|Coffeeof|Teaof|Waterofint//getastringrepresentationofthevalueoverridex.ToString()=matchx|Coffeex->Printf.sprintf"Coffee:%i"|Teax->Printf.sprintf"Tea:%i"|Waterx->Printf.sprintf"Water:%i"http://createanewinstanceofDrinkAmountlett=Tea2//printoutthestringprintfn"%s"(t.ToString())示例運(yùn)行結(jié)果如Tea:注意如何使用關(guān)override替了member,它有替換、覆蓋基類已有函數(shù)的效果F#Finalize)可以覆蓋。每一個(gè).NET類型都從System.Object繼承,由于這些方法與CLR的交互問題,只建議覆蓋ToString。只有四種方法可用于覆蓋,是因?yàn)橛涗浐吐?lián)合類型并不承擔(dān)基類或派生類的作用,因此,不能繼承要覆蓋的方法(System.Object除外)。對象表達(dá)式(Object對象表達(dá)式是用F#簡化面向?qū)ο缶幊痰模峁┝撕喢鞯恼Z法,繼承已有類型,創(chuàng)建對象,可用(<>)括起來。接下來,加關(guān)鍵字with,實(shí)現(xiàn)類、接口的方法定義,這些方法就如同在記錄或聯(lián)合類型上方法一樣(參見前面一節(jié))。每一個(gè)新方法,使用關(guān)鍵字member或override,加起來,并用逗號(hào)分隔,就像NET法一樣(除非方法只有一個(gè)參數(shù),可以不用括號(hào))。通常不需要號(hào),加方法體的實(shí)現(xiàn),是F#表達(dá)式,必須匹配方法的返回值。openopen//acomparerthatwillcomparestringintherereversedorderletcomparer={newIComparer<string> pare(s1,s2)//functiontoreversealetrev(s:String)newString(Array.rev//reverse1ststringletreversed=rev//comparereversedstringto2ndstringsreversedpareTo(revs2)}//Eurovisionwinnersinarandomorderletwinners=[|"SandieShaw";"BucksFizz";"DanaInternational";"Abba";"Lordi"|]//printthewinnersprintfn"%A"http://sortthewinnersArray.Sort(winners,comparer)//printthewinnersagainprintfn"%A"winners運(yùn)行結(jié)果如下[|"SandieShaw";"BucksFizz";"DanaInternational";"Abba";"Lordi"|][|"Abba";"Lordi";"DanaInternational";"SandieShaw";"Bucks前面的例子實(shí)現(xiàn)了一個(gè)接口IComparer,它是只有一個(gè)方Comparer有兩個(gè)參數(shù),返回表示參數(shù)比較結(jié)果的整數(shù)。它接收一個(gè)類型參數(shù),這里是一個(gè)字符串,可以看到,在標(biāo)comparer使用comparer,然后,[用comparer]進(jìn)行排序,輸出前后的結(jié)果到控制臺(tái)。不論是F#是通用語言運(yùn)行時(shí),都不允許類有多個(gè)繼承。不論哪種情況,在第一個(gè)接口、類后面的任interface,后面是關(guān)with。方法的定義與第一個(gè)接口、類相同。如果不改變類中的任何方法,就不需要用關(guān)鍵字with。openopenopen//createanewinstanceofanumbercontrolletmakeNumberControl(n:int)={newTextBox(Tag=n,Width=32,Height=16,Text=//implementtheIComparableinterfacesothe//canbecomparedinterfaceIComparablewith pareTo(other)letotherControl=other:?>Controlinletn1=otherControl.Tag:?>intinpareTo(n1)//asortedarrayofthenumberedcontrolsletnumbers=//initalizethelettemp=new//initalizetherandomnumbergeneratorletrand=newRandom()//addthecontrolscollectionforindex=1to10dotemp.Add(makeNumberControl//sortthecollection//layoutthecontrolscorrectlyletheight=ref0temp|>Seq.iter(func->c.Top<-height:=c.Height+//returncollectionasanarray//createaformtoshowthenumbercontrolsletnumbersForm=lettemp=newForm()in//showtheformdo parable使實(shí)現(xiàn)這個(gè)接口的對象能夠進(jìn)行比較,更好地用來排序。這里,IComparable的CompareTo方法實(shí)現(xiàn)了根據(jù)文本框中文本顯示的數(shù)字排序控件;實(shí)現(xiàn)makeNumberControl函數(shù)之后,創(chuàng)建一個(gè)控制數(shù)組numbers。數(shù)組numbers定義有些復(fù)雜,首先初始化,以隨機(jī)順序充填全部控件,然后,對數(shù)組進(jìn)行排序,最后,確保每個(gè)控件在適當(dāng)?shù)母叨壬巷@示。運(yùn)行結(jié)果如圖5-2所示。圖5-2框控用with。假設(shè)我們不用文本框顯示數(shù)字,而準(zhǔn)備自定義繪制,通過覆蓋對象的OnPaint方法:openopenopen//createanewinstanceofanumbercontrolletmakeNumberControl(n:int)={newControl(Tag=n,Width=32,Height=16)//overridethecontrolspaintmethodtodrawthenumberoverridex.OnPaint(e)=letfont=newFont(FontFamily.Families.[2],12.0F)newPointF(0.0F,//implementtheIComparableinterfacesothe//canbecomparedinterfaceIComparablewith pareTo(other)letotherControl=other:?>Controlinletn1=otherControl.Tag:?>intinpareTo(n1)運(yùn)行結(jié)果如圖5-3圖5-3定義對象表達(dá)式的機(jī)制非常有效,能夠快速、簡F#函數(shù)中的面向?qū)ο蠊δ軐?dǎo)F碼中,放在控件的tag屬性,這會(huì)比正常地解決方案有地工作。然而,在不需要為類型添加額外的屬性或定義我們已經(jīng)看到過一部分使用NETBCL函數(shù)中類的示例了,下面,將學(xué)習(xí)如何定義我們自己的類。在類是類型,因此,類定義使type,加類名,加類的構(gòu)造函數(shù)的參數(shù),放在括號(hào)中再加等號(hào),加類的成員定義。類的最基本的成員稱為方法(method),這是一個(gè)函數(shù),能夠類的參數(shù)。 個(gè)成員方法:Authenticate,用于檢查用戶是否正確,和LogonMessage,用于獲取指定用戶的登open//aclassthatrepresentsa//it'sconstructortakestwoparameters,the//nameandahashoftheirpasswordtypeUser(name,passwordHash)=//hashstheuserspasswordandchecksit//theknownmemberx.Authenticate(password)lethashResult=hash(password,"sha1")passwordHash=hashResult//getstheuserslogonmessagememberx.LogonMessage()=Printf.sprintf"o,%s"http://createanewinstanceofouruserletuser=User("Robert",letmain()//authenticateuserandprintappropriatemessageifuser.Authenticate("badpass")thenprintfn"%s"(user.LogonMessage())printfn"Logonfailed"示例的后半部分演示了如何使用類,其行為就如我們已經(jīng)看過的來自.NETBCL的其他類一樣。我們可以用關(guān)鍵字new創(chuàng)建User的實(shí)例,然后,調(diào)用它的成員方法。共享;或者也可能從外部數(shù)據(jù)源一些對象的數(shù)據(jù)。有些對象可能有只在對象的let,但是,需要在對象的所有成員之間共享,要做到這一點(diǎn),就要把這個(gè)let綁定放在類定義的開頭,即等號(hào)之后,第一個(gè)成員定義之前。let式構(gòu)造,當(dāng)這個(gè)對象構(gòu)造時(shí)執(zhí)行;如果let函數(shù)調(diào)用的前面加關(guān)鍵字do。下面的示例演示了私有l(wèi)et綁定,拿原來的User類,并做稍許修改。現(xiàn)在,類的構(gòu)造函數(shù)用firstName和lastName,在let綁定中生成用戶的全名(fullName)。要想看到調(diào)用有副作用的函數(shù) open//aclassthatrepresentsa//it'sconstructortakesthreeparameters,the//firstname,lastnameandahashoftheirpasswordtypeUser(firstName,lastName,passwordHash)=//calculatetheuser'sfullnameandstoreoflateruseletfullName=Printf.sprintf"%s%s"firstName//printusersfullnameasobjectisbeingconstructeddoprintfn"User:%s"fullName//hashstheuserspasswordandchecksit//theknownmemberx.Authenticate(password)lethashResult=hash(password,"sha1")passwordHash=hashResult//retrievestheusersfullnamememberx.GetFullname()=注意成員還能類的let綁定,成員GetFullName返回已經(jīng)計(jì)算好的fullName值通常需要能夠在類的改變值,比如,可能需要在User類中提供ChangePassword方法重置用戶。F#提供了兩種方法。在處理不可變對象,就對象的參數(shù),改變適當(dāng)?shù)闹怠_@種方法通常是法ChangePassword如何對password數(shù)hash連同用戶名一起,傳遞給User對象的構(gòu)造函數(shù):open//aclassthatrepresentsa//it'sconstructortakestwoparameters,the//nameandahashoftheirpasswordtypeUser(name,passwordHash)=//hashstheuserspasswordandchecksit//theknownmemberx.Authenticate(password)lethashResult=hash(password,"sha1")passwordHash=hashResult//getstheuserslogonmessagememberx.LogonMessage()=Printf.sprintf"o,%s"http://createsacopyoftheuserwiththepasswordchangedmemberx.ChangePassword(password)=newUser(name,hash對于處理不可變對象的另法,是你想改變的值可變,通過把它綁定到可變的let綁定,在下面的示例中可以看到,把類的參數(shù)passwordHash綁定到同名的可變綁定上:open//aclassthatrepresentsa//it'sconstructortakestwoparameters,the//nameandahashoftheirpasswordtypeUser(name,passwordHash)=//storethepasswordhashinamutable//binding,soitcanbechangedlaterletmutablepasswordHash=passwordHash//hashstheuserspasswordandchecksit//theknownmemberx.Authenticate(password)lethashResult=hash(password,"sha1")passwordHash=hashResult//getstheuserslogonmessagememberx.LogonMessage()=Printf.sprintf"o,%s"http://changestheuserspasswordmemberx.ChangePassword(password)=passwordHash<-hash即,你可以自由修改passwordHashlet如ChangePassword中做可選參數(shù)(Optional釋放在參數(shù)名的后面,用分號(hào)隔開;可選參option<'a>因此,不必要放在類型注下面是一個(gè)可選參數(shù)的示例,定義了一個(gè)類AClass,其構(gòu)造函數(shù)有一個(gè)可選的整型參數(shù);它有一個(gè)成員方法PrintState,有兩參數(shù)(第二個(gè)參數(shù)是可選的)。正如我們所想的一樣,用針對option<'a>typeAClass(?someState:int)=letstate=matchsomeState|Somex->string|None->"<nomemberx.PrintState(prefix,?postfix)=matchpostfixwith|Somex->printfn"%s%s%s"prefixstate|None->printfn"%s%s"prefixletaClass=newAClass()letaClass'=newAClass(109)aClass.PrintState("Therewas")aClass'.PrintState("Inputwas:",",whichisnice.")第二個(gè)把值109給構(gòu)造函數(shù);接著PrintState面一個(gè)調(diào)用沒有可選參數(shù),Therewas<noInputwas:109,whichis目前l(fā)et定定義的函數(shù)還不能有可選參數(shù),正在研究,如何在未來版本的語言中,增加函數(shù)的定義接口只能包含抽象方法和屬性,或者使用關(guān)鍵字的方法。接口定義一個(gè)約接口提供了相似于多類繼承(multiple-classinheritance)的行為,但避免了其實(shí)現(xiàn)的復(fù)雜性。它了兩個(gè)方法:Authenticate和LogonMessage。注意,接口名的首字母為I,這個(gè)命名約定嚴(yán)格遵循NETBCL我們在自己的代碼也應(yīng)該遵循這個(gè)規(guī)則,因?yàn)?,它能有助于在閱讀代碼時(shí),其//aninterface"IUser"typeIUser=//hashstheuserspasswordandchecksit//theknownAuthenticate:evidence:string->//getstheuserslogonmessageLogonMessage:unit->stringletlogon(user:IUser)//authenticateuserandprintappropriatemessageifuser.Authenticate("badpass")thenprintfn"%s"(user.LogonMessage())printfn"Logonlogon用IUser錄;然后,這個(gè)函數(shù)處理任何IUser現(xiàn)。這在許多情況下是非常接口實(shí)現(xiàn)接口,使用關(guān)鍵字interface,加接口with,加實(shí)現(xiàn)接口成員的代碼;成員定義的前面member,其他方面與方法或?qū)傩缘亩x相同。實(shí)現(xiàn)接口,可以通過類,也可以用結(jié)構(gòu)。下面的示例演示如何創(chuàng)建、實(shí)現(xiàn)和使用接口。這個(gè)接口與前面一節(jié)中實(shí)現(xiàn)的接口IUser同,這里,在類User中實(shí)現(xiàn)這個(gè)接口:open//aninterface"IUser"typeIUser=//hashstheuserspasswordandchecksit//theknownAuthenticate:evidence:string->//getstheuserslogonmessageLogonMessage:unit->string//aclassthatrepresentsa//it'sconstructortakestwoparameters,the//nameandahashoftheirpasswordtypeUser(name,passwordHash)=interfaceIUser//Authenticateimplementationmemberx.Authenticate(password)=lethashResult=hash(password,"sha1")passwordHash=hashResult//LogonMessageimplementationmemberx.LogonMessage()=Printf.sprintf"o,%s"http://createanewinstanceoftheletuser=User("Robert",//casttotheIUserinterfaceletiuser=user:>IUser//getthelogonletlogonMessage=iuser.LogonMessage()letlogon(iuser:IUser)=//authenticateuserandprintappropriatemessageifiuser.Authenticate("badpass")thenprintfn"%s"logonMessageprintfn"Logonfailed"dologonuser注意在示例的中間我們首次casting(強(qiáng)制類型轉(zhuǎn)換),在本章的最后“類型轉(zhuǎn)換”會(huì)有詳細(xì)討論。但是,這里簡要說明一下:標(biāo)識(shí)符user,通過向下轉(zhuǎn)換運(yùn)算符(:?>)轉(zhuǎn)換成接口IUser://createanewinstanceoftheletuser=User("Robert",//casttotheIUserinterfaceletiuser=user:?>IUser這是必須的,因F#實(shí)現(xiàn)的。在能夠使用方法LogonMessage前,必須有一個(gè)標(biāo)識(shí)符,它不僅是實(shí)現(xiàn)了IUser的類,而且它的類型也要是IUser。向后,到示例的結(jié)束,有不同的解決方案。函數(shù)logon的參數(shù)類型為IUser:letlogon(iuser:IUser)當(dāng)用實(shí)現(xiàn)了IUser調(diào)用logon時(shí),這個(gè)類被隱式向下轉(zhuǎn)換成IUserAuthenticate和LogonMessage?,F(xiàn)在就不再需要強(qiáng)制轉(zhuǎn)換標(biāo)識(shí)符user了(在本章的后面“類和方open//aclassthatrepresentsa//it'sconstructortakestwoparameters,the//nameandahashoftheirpasswordtypeUser(name,passwordHash)=interfaceIUser//Authenticateimplementationmemberx.Authenticate(password)=lethashResult=hash(password,"sha1")passwordHash=hashResult//LogonMessageimplementationmemberx.LogonMessage()=Printf.sprintf"o,%s"http://ExposeAuthenticatememberx.Authenticate(password)=//ExposeLogonMessageimplementationmemberx.LogonMessage()=x.LogonMessage()類和繼承添加新的功能,也可以調(diào)整或替換原有的功能。像大多數(shù)現(xiàn)代面向?qū)ο笳Z言一樣,F(xiàn)#只允許單繼承指定繼承,使用關(guān)鍵字inherit,必須緊跟在等號(hào)后面,加類的構(gòu)造函數(shù)關(guān)鍵字class的后面;在關(guān)鍵字inherit文可能有誤,inheritance面,加想要繼承的類名,加想要傳遞類的構(gòu)造函數(shù)的參數(shù)。讓我們撇開一些細(xì)節(jié),看一個(gè)簡單的兩個(gè)F#型之間繼承的例子。下面示例中有一個(gè)F#sub,從一個(gè)基類Base派生;類Base有一個(gè)方法GetState,類Sub也有一個(gè)方法GetOtherState。這個(gè)例子演示了派生類Sub用兩個(gè)方法,因?yàn)镚etState而來typeBase()memberx.GetState()=typeSub()=inheritBase()memberx.GetOtherState()=0letmyObject=newSub()"myObject.state=%i,myObject.otherState=%i"示例的運(yùn)行結(jié)果如下myObject.state=0,myObject.otherState=方法和繼 鍵字member和 來定義方法;關(guān)鍵字member定義一個(gè)簡單的有實(shí)現(xiàn)的但不能被覆蓋的方 定義的方法沒有實(shí)現(xiàn),必須在派生類中被覆蓋;關(guān)鍵字override定義的方法覆蓋被繼承的、在基類中已經(jīng)實(shí)現(xiàn)的方法;最后,關(guān)鍵字default的意思與override相似,但是,//abaseclasstypeBase()=//someinternalstatefortheclassletmutablestate=0//anordinarymembermethodmemberx.JiggleStatey=state<-y//an WiggleState:int->unit//adefaultimplementationforthe defaultx.WiggleStatey=state<-y+statememberx.GetState()=state//asubclasstypeSub()=inherit//override defaultx.WiggleStatey=x.JiggleState(x.GetState()&&&//createinstancesofbothmethodsletmyBase=newBase()letmySub=new//asmalltestforourclasseslettestBehavior(c:#Base)=c.JiggleStateprintfn"%i"(c.GetState())c.WiggleState3printfn"%i"http://runthetestsletmain()=printfn"baseclass:testBehaviormyBaseprintfn"subclass:"testBehaviormySub運(yùn)行結(jié)果如下base14sub11首先Base實(shí)現(xiàn)JiggleState,這個(gè)方法不能被覆蓋,因此,所有的派生類都繼承了這個(gè)實(shí)現(xiàn);然后,定義抽象方法WiggleState,它可被派生類覆蓋(實(shí)際上,必須覆蓋)。要新定義一個(gè)可以被覆蓋的方法,需要用到關(guān)鍵字和default的組合。就是說,在基類中使用,而在派生類中使用default。然而,它們在同一個(gè)類中通常在一起使用,就如前面的例子一樣。這要求程序員必須為需要被覆蓋的方法顯式指定類型。雖然F#通常并不要求程序員顯式指定類就如上面的結(jié)果所示,當(dāng)調(diào)用JiggleState在基類和派生類中保持相同的行為;相比之下WiggleState于被為了基類中的方法,要使用關(guān)鍵字base。下面的例子實(shí)現(xiàn)一個(gè)類,從System.Windows.Form中派生。使用了隱式類構(gòu)造,類型MySquareForm有一個(gè)參數(shù)color:moduleopenopen//defineaclassthatinheritsfrom'Form'typeMySquareForm(color)=inherit//overridetheOnPaintmethodtodrawontheformoverridex.OnPaint(e)=10,x.Width-x.Height-//overridetheOnResizemethodtorespondtoresizingoverridex.OnResize(e)=//createanewinstanceoftheformletform=newMySquareForm(Pens.Blue)//showthedo[在交互式窗口中使用下面的語句否則,會(huì)出錯(cuò)System.InvalidOperationException:單個(gè)線程上開始另一個(gè)消息循環(huán)是無效操作]在這個(gè)窗體中,我們覆蓋了OnPaintOnResize;在這些方法中,使用了關(guān)鍵字base,它(acesors,包(getaccssos)(staccessrs屬性的定義,與方法相同,用關(guān)鍵字member,加表示對象的參數(shù),加點(diǎn),加成員名;之后,不用方法參數(shù),而是用關(guān)withgetset;后面是參數(shù),get的參數(shù)是空類型,set必須有一個(gè)唯一的參數(shù);加等號(hào),加表達(dá)式,構(gòu)成方法體。如果需要第二個(gè)方法,就用關(guān)鍵字and把它們連下面的例子定義了一個(gè)類,有唯一的屬性MyProp,返回一個(gè)隨機(jī)數(shù)。存屬性重置隨機(jī)數(shù)生成的//aclasswithpropertiestypeProperties()=letmutablerand=new//apropertydefinitionmemberx.MyPropwithget()=andsety=rand<-new//createanewinstanceofourclassletprop=newProperties()//runsometestsfortheclassprop.MyProp<-12printfn"%d"prop.MyPropprintfn"%d"prop.MyPropprintfn"%d"下面是運(yùn)行的結(jié)[]也可以抽象屬性,語法是相似的,只是關(guān)鍵字member換成了 就像在方法中做的一樣;成員名的后面是用冒號(hào)隔開的類型名;加關(guān)鍵字with,之加get或set,表示繼承的方法必須實(shí)現(xiàn)get或set,用逗號(hào)隔開。對于調(diào)用它的代碼來說,屬性跟字段完全一樣。下面的例子是對前面代碼的修改,現(xiàn)在使用接口I ConcreteProperties必須使用關(guān)鍵字with和and實(shí)現(xiàn)get和set方法。//aninterfacewithan typeI Properties=MyProp:intwithget,set//aclassthatimplementsourinterfacetypeConcreteProperties()=letmutablerand=newSystem.Random()interfaceI Propertieswithmemberwithget()=andset(y)=rand<-new[letprop=newConcreteProperties():> //runsometestsfortheclassprop.MyProp<-12printfn"%d"prop.MyPropprintfn"%d"prop.MyPropprintfn"%d"]C#引器在底層實(shí)現(xiàn)上都是Item,但是,程序員們從來就不用這個(gè)名字,因?yàn)樗偸请[含的;在F#中,程序員可以選擇索引器屬性的名字。如果選擇的名字Item,那么,會(huì)提供專門的語法創(chuàng)建索引器的語法與屬性相似,但是,get一個(gè)或多個(gè)參數(shù),而set個(gè)或多個(gè)參//aclasswithtypeIndexers(vals:string[])//anormalindexermemberx.Itemwithgety=andsetyz=vals.[y]<-//anindexerwithanunusualnamememberx.MyStringwithgety=andsetyz=vals.[y]<-//createanewinstanceoftheindexerletindex=newIndexers[|"One";"Two";"Three";//testthesetindexersindex.[0]<-"Five";index.Item(2)<-"Six";index.MyString(3)<-"Seven";//testthegetindexersprintfn"%s"index.[0]printfn"%s"(index.Item(1))printfn"%s"(index.MyString(2))printfn"%s"運(yùn)行結(jié)果如下注意,當(dāng)索引器的名字不是Item時(shí),應(yīng)該記住,其他.Net語言想使用這個(gè)類就很了覆蓋非F#庫中的方覆蓋非F#中的方法,方法定義必須是以元組的形式,就是必須用括號(hào)括起來,用逗號(hào)分隔下面的例子定義的類實(shí)現(xiàn)接口System.Net.ICredentials,它只有一個(gè)方法GetCredential,有兩個(gè)參數(shù),就在接口實(shí)現(xiàn)的后面。如何在方法GetCredentialList中把接口當(dāng)作值使用:[為這個(gè)方法創(chuàng)建一個(gè)F#]typeCredentialsFactory()=classinterfaceSystem.Net.ICredentialswithmemberx.GetCredential(uri,authType)newSystem.Net.NetworkCredential("rob","whatever","F#credentials")memberx.GetCredentialListuriauthTypes=lety=(x:>letgetCredentials=y.GetCredential(uri,s)List.mapgetCredentialauthTypes在第十四章,將學(xué)習(xí)有關(guān)F#與C#簽名之間關(guān)系的內(nèi)容抽象F#義約定(contract)通??山邮艿姆椒ň褪墙涌冢诖蠖鄶?shù)情況下都能很好的工作。但是,實(shí)現(xiàn),需要為抽AbstactClass>]如果選擇使用抽象類,前面的示例User能看起// classthatrepresentsa//it'sconstructortakesone//theuser'sname typeUser(name)=//theimplmentationofthismethodshouldhashs//userspasswordandchecksitagainsttheknownhashAuthenticate:evidence:string->bool//getstheuserslogonmessagememberx.LogonMessage()=Printf.sprintf"o,%s"創(chuàng)建靜態(tài)方法static,加關(guān)鍵字member,加方法名,加參數(shù),加等號(hào),加方法定義。與聲明實(shí)例方法基本相同,只是多了一個(gè)關(guān)鍵字static,少了表示對象的參數(shù)。不要表示對象的參數(shù),是靜態(tài)方法提供了另一種途徑來創(chuàng)建對象的新實(shí)例,F(xiàn)#沒有提供重載類構(gòu)造函數(shù)的功能,因此,提供靜態(tài)方法來調(diào)用類的構(gòu)造函數(shù)。下面再返回到User的示例,這一次增加一個(gè)靜態(tài)方法,根據(jù)數(shù)據(jù)庫中open//aclassthatrepresentsa//it'sconstructortakestwoparameters,the//nameandahashoftheirpasswordtypeUser(name,passwordHash)=//hashstheuserspasswordandchecksit//theknownmemberx.Authenticate(password)lethashResult=hash(password,"sha1")passwordHash=hashResult//getstheuserslogonmessagememberx.LogonMessage()=Printf.sprintf"o,%s"http://astaticmemberthatprovidesterative//ofcreatingtheobjectstaticmemberFromDBidletname,ph=getUserFromDBidnewUser(name,ph)letuser=User.FromDB下面的例子假設(shè)你想在類MyInt中重新實(shí)現(xiàn)整型,并在類中定義加法:typeMyInt(state:int)=classmemberx.State=statestaticmember(+)(x:MyInt,y:MyInt):MyInt=newMyInt(x.State+y.State)overridex.ToString()=stringstateletx=newMyInt(1)lety=newprintfn"(x+y)=%A"(x+運(yùn)行結(jié)果如下(x+y)=有明確字段和構(gòu)造函數(shù)的本章到這里,關(guān)注還只是類的隱式語法,但是,F(xiàn)#還有另一種類型的語法:顯式語法。隱式語法通常更好的原因:第一,往往比顯式語法更短;第二,F(xiàn)#編譯器能夠優(yōu)化這種類。隱式語法通常就是let在顯式語法中,在類型名的后面不需要類的構(gòu)造函數(shù);相反,類型名后直接加等號(hào)class,加類的定義(比如,typeUser=class...)。當(dāng)使用顯式語法時(shí),類的結(jié)尾,用關(guān)鍵字end。定義字段,使用關(guān)鍵字val,加字段名和類型名,之間用冒號(hào)隔開。默認(rèn)情況下,類中的字段是不可變是有用的;這樣,F(xiàn)#鍵字mutable;當(dāng)字段用關(guān)鍵字mutable義,無論程序員什么時(shí)候new,加構(gòu)造函數(shù)[?,好像應(yīng)該是構(gòu)造函數(shù)的參數(shù)],用括號(hào)括起來;加等號(hào),加程序塊(用大括號(hào)括下面的例子,重寫了我們的第一個(gè)類,用顯式語法,定義了簡User碼多open//giveshortenametopasswordhashinglethash=//aclassthatrepresentsa//it'sconstructortakestwoparameters,the//nameandahashoftheirtypeUser=//theclass'fieldsvalname:stringvalpasswordHash://theclass'constructornew(name,passwordHash){name=name;passwordHash=passwordHash//hashstheuserspasswordandchecksit//theknownmemberx.Authenticate(password)lethashResult=hash(password,"sha1")x.passwordHash=hashResult//getstheuserslogonmessagememberx.LogonMessage()=Printf.sprintf o,%s"強(qiáng)制類型轉(zhuǎn)換(pcatin),(dwncatin)F#ob(或Systm.Obect,有子類在的。向轉(zhuǎn)是把型次向?qū)酉孪旅娴拇a是用upcast將string轉(zhuǎn)換到letmyObject=("Thisisastring":>何創(chuàng)建控件數(shù)組,這是使用Windows窗體應(yīng)用程序時(shí)十分常見的任務(wù)。注意,所有單獨(dú)的控件都向上轉(zhuǎn)換成它們通常的基類Control:openSystem.Windows.FormsletmyControls=[|(newButton():>(newTextBox():>Control);(newLabel():>Control)stack),而不是托管堆(managedheap)。裝箱把值壓入托管堆,因此,能夠作為被傳遞。下面letboxedInt=(1:>能出錯(cuò),會(huì)在運(yùn)行時(shí)不正確的強(qiáng)制轉(zhuǎn)換意外(System.InvalidCastException)的問題。由于向下轉(zhuǎn)換固有的性,許多開發(fā)寧可用對.NET類型的模式匹配來取代,如第三章中的演示。盡管openSystem.Windows.FormsletmoreControls=[|(newButton():>Control);(newTextBox():>Control)|]letcontrollettemp=moreControls.[0]temp.Text<-"ClickMe!"letbuttonlettemp=(control:?>temp.DoubleClick.Add(fune->MessageBox.Show("o")|>ignore)這個(gè)例子創(chuàng)建了一個(gè)數(shù)組,有兩個(gè)Windows控件對象,把它們向上轉(zhuǎn)換成基類Control;然后,綁定第一個(gè)控件到標(biāo)control,再把它向下轉(zhuǎn)換成指定的類型Button;再給它的雙擊(DoubleClick)事件添加事件處理程序,Control類上沒有事件。類型面的例子是把字符串綁定到obj類型的標(biāo)識(shí)符上:letmyObject=("Thisisastring":>可以把標(biāo)識(shí)符綁
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度主題餐飲商鋪?zhàn)赓U管理合同
- 二零二五年度邊境運(yùn)輸合同糾紛管轄權(quán)跨境協(xié)議
- 二零二五年度稻谷種植與鄉(xiāng)村旅游融合發(fā)展合同
- 2025年度白酒全國總代理合同-品牌授權(quán)與市場運(yùn)營協(xié)議
- 二零二五年度商業(yè)寫字樓租賃合同終止書
- 2025年度石材行業(yè)市場調(diào)研與咨詢合同
- 2025年度多功能車庫租賃及綜合性服務(wù)合同
- 二零二五年度建筑工拖欠工資無合同糾紛調(diào)解協(xié)議書
- 2025年度采石場租賃合同生態(tài)補(bǔ)償與恢復(fù)協(xié)議
- 2025年度火鍋店線上線下營銷推廣合作協(xié)議
- 2024年供應(yīng)鏈安全培訓(xùn):深入剖析與應(yīng)用
- 飛鼠養(yǎng)殖技術(shù)指導(dǎo)
- 壞死性筋膜炎
- 整式的加減單元測試題6套
- 股權(quán)架構(gòu)完整
- 山東省泰安市2022年初中學(xué)業(yè)水平考試生物試題
- 注塑部質(zhì)量控制標(biāo)準(zhǔn)全套
- 銀行網(wǎng)點(diǎn)服務(wù)禮儀標(biāo)準(zhǔn)培訓(xùn)課件
- 二年級(jí)下冊數(shù)學(xué)教案 -《數(shù)一數(shù)(二)》 北師大版
- 晶體三極管資料
- 石群邱關(guān)源電路(第1至7單元)白底課件
評(píng)論
0/150
提交評(píng)論