国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 開(kāi)發(fā) > 綜合 > 正文

C#銳利體驗(yàn)之第六講 方法

2024-07-21 02:29:45
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

方法又稱成員函數(shù)(member function),集中體現(xiàn)了類或?qū)ο蟮男袨椤7椒ㄍ瑯臃譃殪o態(tài)方法和實(shí)例方法。靜態(tài)方法只可以操作靜態(tài)域,而實(shí)例方法既可以操作實(shí)例域,也可以操作靜態(tài)域--雖然這不被推薦,但在某些特殊的情況下會(huì)顯得很有用。方法也有如域一樣的5種存取修飾符--public,protected,internal,protected internal,private,它們的意義如前所述。

方法參數(shù)

方法的參數(shù)是個(gè)值得特別注意的地方。方法的參數(shù)傳遞有四種類型:傳值(by value),傳址(by reference),輸出參數(shù)(by output),數(shù)組參數(shù)(by array)。傳值參數(shù)無(wú)需額外的修飾符,傳址參數(shù)需要修飾符ref,輸出參數(shù)需要修飾符out,數(shù)組參數(shù)需要修飾符params。傳值參數(shù)在方法調(diào)用過(guò)程中如果改變了參數(shù)的值,那么傳入方法的參數(shù)在方法調(diào)用完成以后并不因此而改變,而是保留原來(lái)傳入時(shí)的值。傳址參數(shù)恰恰相反,如果方法調(diào)用過(guò)程改變了參數(shù)的值,那么傳入方法的參數(shù)在調(diào)用完成以后也隨之改變。實(shí)際上從名稱上我們可以清楚地看出兩者的含義--傳值參數(shù)傳遞的是調(diào)用參數(shù)的一份拷貝,而傳址參數(shù)傳遞的是調(diào)用參數(shù)的內(nèi)存地址,該參數(shù)在方法內(nèi)外指向的是同一個(gè)存儲(chǔ)位置。看下面的例子及其輸出:

using system;class test{static void swap(ref int x, ref int y) {int temp = x;x = y;y = temp;}static void swap(int x,int y) {int temp = x;x = y;y = temp;}static void main() {int i = 1, j = 2;swap(ref i, ref j);console.writeline("i = {0}, j = {1}", i, j);swap(i,j);console.writeline("i = {0}, j = {1}", i, j);}}
程序經(jīng)編譯后執(zhí)行輸出:

i = 2, j = 1
i = 2, j = 1
我們可以清楚地看到兩個(gè)交換函數(shù)swap()由于參數(shù)的差別--傳值與傳址,而得到不同的調(diào)用結(jié)果。注意傳址參數(shù)的方法調(diào)用無(wú)論在聲明時(shí)還是調(diào)用時(shí)都要加上ref修飾符。

籠統(tǒng)地說(shuō)傳值不會(huì)改變參數(shù)的值在有些情況下是錯(cuò)誤的,我們看下面一個(gè)例子:

using system;class element{public int number=10;}class test{static void change(element s){s.number=100;}static void main() {element e=new element();console.writeline(e.number);change(e);console.writeline(e.number);}}
程序經(jīng)編譯后執(zhí)行輸出:

10
100
我們看到即使傳值方式仍然改變了類型為element類的對(duì)象t。但嚴(yán)格意義上講,我們是改變了對(duì)象t的域,而非對(duì)象t本身。我們?cè)倏聪旅娴睦樱?/p>

using system;class element{public int number=10;}class test{static void change(element s){element r=new element();r.number=100;s=r;}static void main() {element e=new element();console.writeline(e.number);change(e);console.writeline(e.number);}}
程序經(jīng)編譯后執(zhí)行輸出:

10
10
傳值方式根本沒(méi)有改變類型為element類的對(duì)象t!實(shí)際上,如果我們能夠理解類這一c#中的引用類型(reference type)的特性,我們便能看出上面兩個(gè)例子差別!在傳值過(guò)程中,引用類型本身不會(huì)改變(t不會(huì)改變),但引用類型內(nèi)含的域卻會(huì)改變(t.number改變了)!c#語(yǔ)言的引用類型有:object類型(包括系統(tǒng)內(nèi)建的class類型和用戶自建的class類型--繼承自object類型),string類型,interface類型,array類型,delegate類型。它們?cè)趥髦嫡{(diào)用中都有上面兩個(gè)例子展示的特性。

在傳值和傳址情況下,c#強(qiáng)制要求參數(shù)在傳入之前由用戶明確初始化,否則編譯器報(bào)錯(cuò)!但我們?nèi)绻幸粋€(gè)并不依賴于參數(shù)初值的函數(shù),我們只是需要函數(shù)返回時(shí)得到它的值是該怎么辦呢?往往在我們的函數(shù)返回值不至一個(gè)時(shí)我們特別需要這種技巧。答案是用out修飾的輸出參數(shù)。但需要記住輸出參數(shù)與通常的函數(shù)返回值有一定的區(qū)別:函數(shù)返回值往往存在堆棧里,在返回時(shí)彈出;而輸出參數(shù)需要用戶預(yù)先制定存儲(chǔ)位置,也就是用戶需要提前聲明變量--當(dāng)然也可以初始化。看下面的例子:

using system;class test{static void resolutename(string fullname,out string firstname,out string lastname) {string[] strarray=fullname.split(new char[]{' '});firstname=strarray[0];lastname=strarray[1];}public static void main() {string myname="cornfield lee";string myfirstname,mylastname;resolutename(myname,out myfirstname,out mylastname);console.writeline("my first name: {0}, my last name: {1}", myfirstname, mylastname);}}
程序經(jīng)編譯后執(zhí)行輸出:

my first name: cornfield, my last name: lee
在函數(shù)體內(nèi)所有輸出參數(shù)必須被賦值,否則編譯器報(bào)錯(cuò)!out修飾符同樣應(yīng)該應(yīng)用在函數(shù)聲明和調(diào)用兩個(gè)地方,除了充當(dāng)返回值這一特殊的功能外,out修飾符ref修飾符有很相似的地方:傳址。我們可以看出c#完全擯棄了傳統(tǒng)c/c++語(yǔ)言賦予程序員莫大的自由度,畢竟c#是用來(lái)開(kāi)發(fā)高效的下一代網(wǎng)絡(luò)平臺(tái),安全性--包括系統(tǒng)安全(系統(tǒng)結(jié)構(gòu)的設(shè)計(jì))和工程安全(避免程序員經(jīng)常犯的錯(cuò)誤)是它設(shè)計(jì)時(shí)的重要考慮,當(dāng)然我們看到c#并沒(méi)有因?yàn)榘踩远鴨适Ф嗌僬Z(yǔ)言的性能,這正是c#的卓越之處,“sharp”之處!

數(shù)組參數(shù)也是我們經(jīng)常用到的一個(gè)地方--傳遞大量的數(shù)組集合參數(shù)。我們先看下面的例子:

using system;class test{static int sum(params int[] args){int s=0;foreach(int n in args){s+=n;}return s;}static void main() {int[] var=new int[]{1,2,3,4,5};console.writeline("the sum:"+sum(var));console.writeline("the sum:"+sum(10,20,30,40,50));}}
程序經(jīng)編譯后執(zhí)行輸出:

the sum:15
the sum:150
可以看出,數(shù)組參數(shù)可以是數(shù)組如:var,也可以是能夠隱式轉(zhuǎn)化為數(shù)組的參數(shù)如:10,20,30,40,50。這為我們的程序提供了很高的擴(kuò)展性。

同名方法參數(shù)的不同會(huì)導(dǎo)致方法出現(xiàn)多態(tài)現(xiàn)象,這又叫重載(overloading)方法。需要指出的是編譯器是在編譯時(shí)便綁定了方法和方法調(diào)用。只能通過(guò)參數(shù)的不同來(lái)重載方法,其他的不同(如返回值)不能為編譯器提供有效的重載信息。

方法繼承
第一等的面向?qū)ο髾C(jī)制為c#的方法引入了virtual,override,sealed,abstract四種修飾符來(lái)提供不同的繼承需求。類的虛方法是可以在該類的繼承自類中改變其實(shí)現(xiàn)的方法,當(dāng)然這種改變僅限于方法體的改變,而非方法頭(方法聲明)的改變。被子類改變的虛方法必須在方法頭加上override來(lái)表示。當(dāng)一個(gè)虛方法被調(diào)用時(shí),該類的實(shí)例--亦即對(duì)象的運(yùn)行時(shí)類型(run-time type)來(lái)決定哪個(gè)方法體被調(diào)用。我們看下面的例子:

using system;class parent{public void f() { console.writeline("parent.f"); }public virtual void g() { console.writeline("parent.g"); }}class child: parent{new public void f() { console.writeline("child.f"); }public override void g() { console.writeline("child.g"); }}class test{static void main() {child b = new child();parent a = b;a.f();b.f();a.g();b.g();}}
程序經(jīng)編譯后執(zhí)行輸出:

parent.f
child.f
child.g
child.g
我們可以看到class child中f()方法的聲明采取了重寫(new)的辦法來(lái)屏蔽class parent中的非虛方法f()的聲明。而g()方法就采用了覆蓋(override)的辦法來(lái)提供方法的多態(tài)機(jī)制。需要注意的是重寫(new)方法和覆蓋(override)方法的不同,從本質(zhì)上講重寫方法是編譯時(shí)綁定,而覆蓋方法是運(yùn)行時(shí)綁定。值得指出的是虛方法不可以是靜態(tài)方法--也就是說(shuō)不可以用static和virtual同時(shí)修飾一個(gè)方法,這由它的運(yùn)行時(shí)類型辨析機(jī)制所決定。override必須和virtual配合使用,當(dāng)然也不能和static同時(shí)使用。

那么我們?nèi)绻谝粋€(gè)類的繼承體系中不想再使一個(gè)虛方法被覆蓋,我們?cè)撛鯓幼瞿兀看鸢甘莝ealed override (密封覆蓋),我們將sealed和override同時(shí)修飾一個(gè)虛方法便可以達(dá)到這種目的:sealed override public void f()。注意這里一定是sealed和override同時(shí)使用,也一定是密封覆蓋一個(gè)虛方法,或者一個(gè)被覆蓋(而不是密封覆蓋)了的虛方法。密封一個(gè)非虛方法是沒(méi)有意義的,也是錯(cuò)誤的。看下面的例子:

//sealed.cs// csc /t:library sealed.csusing system;class parent{public virtual void f() {console.writeline("parent.f");}public virtual void g() {console.writeline("parent.g");}}class child: parent{sealed override public void f() {console.writeline("child.f");} override public void g() {console.writeline("child.g");} }class grandson: child{override public void g() {console.writeline("grandson.g");} }
抽象(abstract)方法在邏輯上類似于虛方法,只是不能像虛方法那樣被調(diào)用,而只是一個(gè)接口的聲明而非實(shí)現(xiàn)。抽象方法沒(méi)有類似于{…}這樣的方法實(shí)現(xiàn),也不允許這樣做。抽象方法同樣不能是靜態(tài)的。含有抽象方法的類一定是抽象類,也一定要加abstract類修飾符。但抽象類并不一定要含有抽象方法。繼承含有抽象方法的抽象類的子類必須覆蓋并實(shí)現(xiàn)(直接使用override)該方法,或者組合使用abstract override使之繼續(xù)抽象,或者不提供任何覆蓋和實(shí)現(xiàn)。后兩者的行為是一樣的。看下面的例子:

//abstract1.cs// csc /t:library abstract1.csusing system;abstract class parent{public abstract void f();public abstract void g();}abstract class child: parent{public abstract override void f();}abstract class grandson: child{public override void f(){console.writeline("grandson.f");}public override void g(){console.writeline("grandson.g");}}
抽象方法可以抽象一個(gè)繼承來(lái)的虛方法,我們看下面的例子:

//abstract2.cs// csc /t:library abstract2.csusing system;class parent{public virtual void method(){console.writeline("parent.method");}}abstract class child: parent{public abstract override void method();}abstract class grandson: child{public override void method(){console.writeline("grandson.method");}}
歸根結(jié)底,我們抓住了運(yùn)行時(shí)綁定和編譯時(shí)綁定的基本機(jī)理,我們便能看透方法呈現(xiàn)出的種種overload,virtual,override,sealed,abstract等形態(tài),我們才能運(yùn)用好方法這一利器!

外部方法

c#引入了extern修飾符來(lái)表示外部方法。外部方法是用c#以外的語(yǔ)言實(shí)現(xiàn)的方法如win32 api函數(shù)。如前所是外部方法不能是抽象方法。我們看下面的一個(gè)例子:

using system;using system.runtime.interopservices;class myclass{[dllimport("user32.dll")]static extern int messageboxa(int hwnd, string msg,string caption, int type);public static void main() {messageboxa(0, "hello, world!", "this is called from a c# app!", 0);}}
程序經(jīng)編譯后執(zhí)行輸出:

這里我們調(diào)用了win32 api函數(shù)int messageboxa(int hwnd, string msg,string caption, int type)。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 中江县| 奉化市| 泰来县| 抚宁县| 宜兰市| 沧州市| 延安市| 淄博市| 海安县| 沈阳市| 越西县| 吉木乃县| 呼玛县| 合阳县| 晋城| 碌曲县| 隆林| 本溪| 麻栗坡县| 慈溪市| 观塘区| 齐河县| 建湖县| 大连市| 将乐县| 宁安市| 五峰| 东海县| 金溪县| 余庆县| 贵溪市| 吴旗县| 札达县| 新闻| 崇仁县| 瓮安县| 雅江县| 榆林市| 清水县| 茶陵县| 库车县|