C#銳利體驗-第六講 方法)

來源:互聯網
上載者:User
第六講 方法


南京郵電學院 李建忠(cornyfield@263.net)

索引


C#銳利體驗

"Hello,World!"程式

C#語言基礎介紹

Microsoft.NET平台基礎構造

類與對象

構造器與析構器

方法

域與屬性

索引器與操作符重載

數組與字串

特徵與映射

COM互操作 非託管編程與異常處理

用C#編織未來--C#編程模型概述

方法又稱成員函數(Member Function),集中體現了類或對象的行為。方法同樣分為靜態方法和執行個體方法。靜態方法只可以操作靜態域,而執行個體方法既可以操作執行個體域,也可以操作靜態域--雖然這不被推薦,但在某些特殊的情況下會顯得很有用。方法也有如域一樣的5種存取修飾符--public,protected,internal,protected internal,private,它們的意義如前所述。

方法參數

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

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);}}

程式經編譯後執行輸出:

    i = 2, j = 1
    i = 2, j = 1

我們可以清楚地看到兩個交換函數Swap()由於參數的差別--傳值與傳址,而得到不同的調用結果。注意傳址參數的方法調用無論在聲明時還是調用時都要加上ref修飾符。

籠統地說傳值不會改變參數的值在有些情況下是錯誤的,我們看下面一個例子:

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);    }    }    

程式經編譯後執行輸出:

    10
    100

我們看到即使傳值方式仍然改變了類型為Element類的對象t。但嚴格意義上講,我們是改變了對象t的域,而非對象t本身。我們再看下面的例子:

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);        }        }        

程式經編譯後執行輸出:

    10
    10

傳值方式根本沒有改變類型為Element類的對象t!實際上,如果我們能夠理解類這一C#中的參考型別(reference type)的特性,我們便能看出上面兩個例子差別!在傳值過程中,參考型別本身不會改變(t不會改變),但參考型別內含的域卻會改變(t.Number改變了)!C#語言的參考型別有:object類型(包括系統內建的class類型和使用者自建的class類型--繼承自object類型),string類型,interface類型,array類型,delegate類型。它們在傳值調用中都有上面兩個例子展示的特性。

在傳值和傳址情況下,C#強制要求參數在傳入之前由使用者明確初始化,否則編譯器報錯!但我們如果有一個並不依賴於參數初值的函數,我們只是需要函數返回時得到它的值是該怎麼辦呢?往往在我們的函數傳回值不至一個時我們特別需要這種技巧。答案是用out修飾的輸出參數。但需要記住輸出參數與通常的函數傳回值有一定的區別:函數傳回值往往存在堆棧裡,在返回時彈出;而輸出參數需要使用者預先制定儲存位置,也就是使用者需要提前聲明變數--當然也可以初始化。看下面的例子:

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);            }            }            

程式經編譯後執行輸出:

    My first name: Cornfield, My last name: Lee

在函數體內所有輸出參數必須被賦值,否則編譯器報錯!out修飾符同樣應該應用在函式宣告和調用兩個地方,除了充當傳回值這一特殊的功能外,out修飾符ref修飾符有很相似的地方:傳址。我們可以看出C#完全擯棄了傳統C/C++語言賦予程式員莫大的自由度,畢竟C#是用來開發高效的下一代網路平台,安全性--包括系統安全(系統結構的設計)和工程安全(避免程式員經常犯的錯誤)是它設計時的重要考慮,當然我們看到C#並沒有因為安全性而喪失多少語言的效能,這正是C#的卓越之處,“Sharp”之處!

數組參數也是我們經常用到的一個地方--傳遞大量的數組集合參數。我們先看下面的例子:

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));                }                }                

程式經編譯後執行輸出:

    The Sum:15
    The Sum:150

可以看出,數組參數可以是數組如:var,也可以是能夠隱式轉化為數組的參數如:10,20,30,40,50。這為我們的程式提供了很高的擴充性。

同名方法參數的不同會導致方法出現多態現象,這又叫重載(overloading)方法。需要指出的是編譯器是在編譯時間便綁定了方法和方法調用。只能通過參數的不同來重載方法,其他的不同(如傳回值)不能為編譯器提供有效重載資訊。

方法繼承
第一等的物件導向機製為C#的方法引入了virtual,override,sealed,abstract四種修飾符來提供不同的繼承需求。類的虛方法是可以在該類的繼承自類中改變其實現的方法,當然這種改變僅限於方法體的改變,而非方法頭(方法聲明)的改變。被子類改變的虛方法必須在方法頭加上override來表示。當一個虛方法被調用時,該類的執行個體--亦即對象的運行時類型(run-time type)來決定哪個方法體被調用。我們看下面的例子:

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();                    }                    }                    

程式經編譯後執行輸出:

    Parent.F
    Child.F
    Child.G
    Child.G

我們可以看到class Child中F()方法的聲明採取了重寫(new)的辦法來屏蔽class Parent中的非虛方法F()的聲明。而G()方法就採用了覆蓋(override)的辦法來提供方法的多態機制。需要注意的是重寫(new)方法和覆蓋(override)方法的不同,從本質上講重寫方法是編譯時間綁定,而覆蓋方法是運行時綁定。值得指出的是虛方法不可以是靜態方法--也就是說不可以用static和virtual同時修飾一個方法,這由它的運行時類型辨析機制所決定。override必須和virtual配合使用,當然也不能和static同時使用。

那麼我們如果在一個類的繼承體系中不想再使一個虛方法被覆蓋,我們該怎樣做呢?答案是sealed override (密封覆蓋),我們將sealed和override同時修飾一個虛方法便可以達到這種目的:sealed override public void F()。注意這裡一定是sealed和override同時使用,也一定是密封覆蓋一個虛方法,或者一個被覆蓋(而不是密封覆蓋)了的虛方法。密封一個非虛方法是沒有意義的,也是錯誤的。看下面的例子:

//sealed.cs                        // csc /t:library sealed.cs                        using 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)方法在邏輯上類似於虛方法,只是不能像虛方法那樣被調用,而只是一個介面的聲明而非實現。抽象方法沒有類似於{…}這樣的方法實現,也不允許這樣做。抽象方法同樣不能是靜態。含有抽象方法的類一定是抽象類別,也一定要加abstract類修飾符。但抽象類別並不一定要含有抽象方法。繼承含有抽象方法的抽象類別的子類必須覆蓋並實現(直接使用override)該方法,或者組合使用abstract override使之繼續抽象,或者不提供任何覆蓋和實現。後兩者的行為是一樣的。看下面的例子:

//abstract1.cs                        // csc /t:library abstract1.cs                        using 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");                        }                        }                        

抽象方法可以抽象一個繼承來的虛方法,我們看下面的例子:

//abstract2.cs                        // csc /t:library abstract2.cs                        using 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");                        }                        }                        

歸根結底,我們抓住了運行時綁定和編譯時間綁定的基本機理,我們便能看透方法呈現出的種種overload,virtual,override,sealed,abstract等形態,我們才能運用好方法這一利器!

外部方法

C#引入了extern修飾符來表示外部方法。外部方法是用C#以外的語言實現的方法如Win32 API函數。如前所是外部方法不能是抽象方法。我們看下面的一個例子:

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);                        }                        }                        

程式經編譯後執行輸出:

這裡我們調用了Win32 API函數int MessageBoxA(int hWnd, string msg,string caption, int type)。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.