> 第五章 類(rainbow 翻譯)*1 (來自重粒子空間)

來源:互聯網
上載者:User
<<展現C#>> 第五章 類(rainbow 翻譯)
出處:http://www.informit.com\matter\ser0000002
本文:
第五章  類
       前一章討論了資料類型和它們的用法。現在我們轉移到C#中至關重要的結構——類。沒有了類,就連簡單的C#程式都不能編譯。這一章假定你知道了一個類的基本組成部分:方法、屬性、建構函式和解構函式。 C#在其中增加了索引和事件。
       在這一章中,你學到下列有關類的話題。
      。 使用建構函式和解構函式
      。給類寫方法
      。給一個類增加屬性存取標誌
      。實現索引
      。建立事件並通過代表元為事件關聯客戶
      。應用類、成員和存取修飾符。

5.1  建構函式和解構函式
          在你可以訪問一個類的方法、屬性或任何其它東西之前, 第一條執行的語句是包含有相應類的建構函式。甚至你自己不寫一個建構函式,也會有一個預設的建構函式提供給你。

class TestClass
{
public TestClass(): base() {} // 由編譯器提供
}

       一個建構函式總是和它的類名相同,但是,它沒有聲明傳回型別。總之,建構函式總是public的,你可以用它們來初始設定變數。

public TestClass()
{
// 在這給變數
// 初始化代碼等等。
}

       如果類僅包含靜態成員(能以類型調用,而不是以執行個體調用的成員),你可以建立一個private的建構函式。
private TestClass() {}
       儘管存取修飾符在這一章的後面將要大篇幅地討論,但是private意味著從類的外面不可能訪問該建構函式。所以,它不能被調用,且沒有對象可以自該類定義被執行個體化。
       並不僅限於無參數建構函式——你可以傳遞初始參數來初始化成員。
       public TestClass(string strName, int nAge) { ... }

       作為一個C/C++程式員,你可能習慣於給初始化寫一個附加的方法,因為在建構函式中沒有傳回值。當然,儘管在C#中也沒有傳回值,但你可以引發一個自製的異常,以從建構函式獲得傳回值。更多有關異常處理的知識在第七章 "異常處理"中有討論。
       但是,當你保留引用給寶貴的資源,應該想到寫一個方法來解決:一個可以被顯式地調用來釋放這些資源。問題是當你可以在解構函式(以類名的前面加"~"的方式命名)中做同樣的事情時,為何還要寫一個附加的方法.
public ~TestClass()
{
// 清除
}

    你應該寫一個附加方法的原因是垃圾收集器,它在變數超出範圍後並不會立即被調用,而僅當間歇期間或記憶體條件滿足時才被觸發。當你鎖住資源的時間長於你所計劃的時間時,它就會發生。因此,提供一個顯式的釋放方式是一個好主意,它同樣能從解構函式中調用。

public void Release()
{
// 釋放所有寶貴的資源
}

public ~TestClass()
{
Release();
}

    調用解構函式中的釋放方法並不是必要的——總之,垃圾收集會留意釋放對象。但沒有忘記清除是一種良好的習慣。

5.2  方法
     既然對象能正確地初始化和結束,所剩下來的就是往類中增加功能。在大多數情況下,功能的主要部分在方法中能得到實現。你早已見過靜態方法的使用,但是,這些是類型(類)的部分,不是執行個體(對象)。
    為了讓你迅速入門,我把這些方法的煩瑣問題安排為三節:
。方法參數
。改寫方法
。方法屏蔽
5.2.1  方法參數
  因方法要處理更改數值,你多多少少要傳遞值給方法,並從方法獲得傳回值。以下三個部分涉及到由傳遞值和為調用者擷取返回結果所引起的問題。

。輸入參數
。引用參數
。輸出參數

5.2.1.1  輸入參數
  你早已在例子中見過的一個參數就是輸入參數。你用一個輸入參數通過值傳遞一個變數給一個方法——方法的變數被調用者傳遞進來的值的一個拷貝初始化。清單5.1 示範輸入參數的使用。

清單  5.1 通過值傳遞參數

1: using System;
2:
3: public class SquareSample
4: {
5:  public int CalcSquare(int nSideLength)
6:  {
7:   return nSideLength*nSideLength;
8:  }
9: }
10:
11: class SquareApp
12: {
13:  public static void Main()
14:  {
15:   SquareSample sq = new SquareSample();
16:   Console.WriteLine(sq.CalcSquare(25).ToString());
17:  }
18: }

   因為我傳遞值而不是引用給一個變數,所以當調用方法時(見第16行),可以使用一個常量運算式(25)。整型結果被傳回給調用者作為傳回值,它沒有存到中間變數就被立即顯示到螢幕上 。
    輸入參數按C/C++程式員早已習慣的工作方式工作。如果你來自VB,請注意沒有能被編譯器處理的隱式ByVal或ByRef——如果沒有設定,參數總是用值傳遞。
     這點似乎與我前面所陳述的有衝突:對於一些變數類型,用值傳遞實際上意味著用引用傳遞。迷惑嗎? 一點背景知識也不需要:COM中的東西就是介面,每一個類可以擁有一個或多個介面。一個介面只不過是一組函數指標,它不包含資料。重複該數組會浪費很多記憶體資源;所以,僅開始地址被拷貝給方法,它作為調用者,仍然指向介面的相同指標。那就是為什麼對象用值傳遞一個引用。

5.2.1.2  引用參數
    儘管可以利用輸入參數和傳回值建立很多方法,但你一想到要傳遞值並原地修改它(也就是在相同的記憶體位置),就沒有那麼好運了。這裡用引用參數就很方便。
void myMethod(ref int nInOut)
   因為你傳遞了一個變數給該方法(不僅僅是它的值),變數必須被初始化。否則,編譯器會警示。清單 5.2 顯示如何用一個引用參數建立一個方法。

清單 5.2  通過引用傳遞參數

1: // class SquareSample
2: using System;
3:
4: public class SquareSample
5: {
6:  public void CalcSquare(ref int nOne4All)
7:  {
8:   nOne4All *= nOne4All;
9:  }
10: }
11:
12: class SquareApp
13: {
14:  public static void Main()
15:  {
16:   SquareSample sq = new SquareSample();
17:
18:   int nSquaredRef = 20; // 一定要初始化
19:   sq.CalcSquare(ref nSquaredRef);
20:   Console.WriteLine(nSquaredRef.ToString());
21:  }
22: }

   正如所看到的,所有你要做的就是給定義和調用都加上ref限定符。因為變數通過引用傳遞,你可以用它來計算出結果並傳回該結果。但是,在現實的應用程式中,我強烈建議要用兩個變數,一個輸入參數和一個引用參數。

5.2.1.3  輸出參數
   傳遞參數的第三種選擇就是把它設作一個輸出參數。正如該名字所暗示,一個輸出參數僅用於從方法傳遞迴一個結果。它和引用參數的另一個區別在於:調用者不必先初始設定變數才調用方法。這顯示在清單5.3中。

清單  5.3  定義一個輸出參數

1: using System;
2:
3: public class SquareSample
4: {
5:  public void CalcSquare(int nSideLength, out int nSquared)
6:  {
7:   nSquared = nSideLength * nSideLength;
8:  }
9: }
10:
11: class SquareApp
12: {
13:  public static void Main()
14:  {
15:   SquareSample sq = new SquareSample();
16:   
17:   int nSquared; // 不必初始化
18:   sq.CalcSquare(15, out nSquared);
19:   Console.WriteLine(nSquared.ToString());
20:  }
21: }


5.2.2  改寫方法
    物件導向設計的重要原則就是多態性。不要理會高深的理論,多態性意味著:當基類程式員已設計好用於改寫的方法時,在衍生類別中,你就可以重定義(改寫)基類的方法。基類程式員可以用 virtual 關鍵字設計方法:
virtual void CanBOverridden()
    當從基類派生時,所有你要做的就是在新方法中加入override關鍵字:
override void CanBOverridden()
    當改寫一個基類的方法時,你必須明白,不能改變方法的訪問屬性——在這章的後面,你會學到更多關於存取修飾詞的知識。
    除了改寫基類方法的事實外,還有另一個甚至更重要的改寫特性。當把衍生類別強制轉換成基類類型並接著調用虛擬方法時,被調用的是衍生類別的方法而不是基類的方法。
((BaseClass)DerivedClassInstance).CanBOverridden();
       為了示範虛擬方法的概念,清單 5.4 顯示如何建立一個三角形基類,它擁有一個可以被改寫的成員方法(ComputeArea)。

清單 5.4   改寫一個基類的方法

1: using System;
2:
3: class Triangle
4: {
5:  public virtual double ComputeArea(int a, int b, int c)
6:  {
7:   // Heronian formula
8:   double s = (a + b + c) / 2.0;
9:   double dArea = Math.Sqrt(s*(s-a)*(s-b)*(s-c));
10:   return dArea;
11:  }
12: }
13:
14: class RightAngledTriangle:Triangle
15: {
16:  public override double ComputeArea(int a, int b, int c)
17:  {
18:   double dArea = a*b/2.0;
19:   return dArea;
20:  }
21: }
22:
23: class TriangleTestApp
24: {
25:  public static void Main()
26:  {
27:   Triangle tri = new Triangle();
28:   Console.WriteLine(tri.ComputeArea(2, 5, 6));
29:   
30:   RightAngledTriangle rat = new RightAngledTriangle();
31:   Console.WriteLine(rat.ComputeArea(3, 4, 5));
32:  }
33: }

    基類Triangle定義了方法ComputeArea。它採用三個參數,返回一個double結果,且具有公用訪問性。從Triangle類派生出的是RightAngledTriangle,它改寫了ComputeArea 方法,並實現了自己的面積計算公式。兩個類都被執行個體化,且在命名為TriangleTestApp的應用類的Main() 方法中得到驗證。
我漏瞭解釋第14行:
class RightAngledTriangle : Triangle
    在類語句中冒號(:)表示RightAngledTriangle從類 Triangle派生。那就是你所必須要做的,以讓C#知道你想把 Triangle當作RightAngledTriangle的基類。
     當仔細觀察直角三角形的ComputeArea方法時,你會發現第3個參數並沒有用於計算。但是,利用該參數就可以驗證是否是“直角”。如清單5.5所示。

清單 5.5   調用基類實現

1: class RightAngledTriangle:Triangle
2: {
3:  public override double ComputeArea(int a, int b, int c)
4:  {
5:   const double dEpsilon = 0.0001;
6:   double dArea = 0;
7:   if (Math.Abs((a*a + b*b - c*c)) > dEpsilon)
8:   {
9:    dArea = base.ComputeArea(a,b,c);
10:   }
11:   else
12:   {
13:    dArea = a*b/2.0;
14:   }
15:
16:   return dArea;
17:  }
18: }

  該檢測簡單地利用了畢達哥拉斯公式,對於直角三角形,檢測結果必須為0。如果結果不為0,類就調用它基類的 ComputeArea來實現。
dArea = base.ComputeArea(a,b,c);
  例子的要點為:通過顯式地利用基類的資格檢查,你就能輕而易舉地調用基類實現改寫方法。
當你需要實現其在基類中的功能,而不願意在改寫方法中重複它時,這就非常有協助。

5.2.3 方法屏蔽
    重定義方法的一個不同手段就是要屏蔽基類的方法。當從別人提供的類衍生類別時,這個功能特別有價值。看清單 5.6,假設BaseClass由其他人所寫,而你從它派生出 DerivedClass 。

清單 5.6   Derived Class 實現一個沒有包含於 Base Class中的方法

1: using System;
2:
3: class BaseClass
4: {
5: }
6:
7: class DerivedClass:BaseClass
8: {
9:  public void TestMethod()
10:  {
11:   Console.WriteLine("DerivedClass::TestMethod");
12:  }
13: }
14:
15: class TestApp
16: {
17:  public static void Main()
18:  {
19:   DerivedClass test = new DerivedClass();
20:   test.TestMethod();
21:  }
22: }

    在這個例子中, DerivedClass 通過TestMethod()實現了一個額外的功能。但是,如果基類的開發人員認為把TestMethod()放在基類中是個好主意,並使用相同的名字實現它時,會出現什麼問題呢?(見清單5.7)

清單 5.7    Base Class 實現和 Derived Class相同的方法

1: class BaseClass
2: {
3:  public void TestMethod()
4:  {
5:   Console.WriteLine("BaseClass::TestMethod");
6:  }
7: }
8:
9: class DerivedClass:BaseClass
10: {
11:  public void TestMethod()
12:  {
13:   Console.WriteLine("DerivedClass::TestMethod");
14:  }
15: }

在優秀的程式設計語言中,你現在會遇到一個真正的大麻煩。但是,C#會給你提出警告:
hiding2.cs(13,14): warning CS0114: 'DerivedClass.TestMethod()' hides inherited member 'BaseClass.TestMethod()'. To make the current method override that implementation, add the override keyword. Otherwise add the new keyword.
(hiding2.cs(13,14):警告  CS0114:'DerivedClass.TestMethod()' 屏蔽了所繼承的成員'BaseClass.TestMethod()'。要想使當前方法改寫原來的實現,加上 override關鍵字。否則加上新的關鍵字。)
具有了修飾符new,你就可以告訴編譯器,不必重寫衍生類別或改變使用到衍生類別的代碼,你的方法就能屏蔽新加入的基類方法。清單5.8  顯示如何在例子中運用new修飾符。

清單  5.8   屏蔽基類方法

1: class BaseClass
2: {
3:  public void TestMethod()
4:  {
5:   Console.WriteLine("BaseClass::TestMethod");
6:  }
7: }
8:
9: class DerivedClass:BaseClass
10: {
11:  new public void TestMethod()
12:  {
13:   Console.WriteLine("DerivedClass::TestMethod");
14:  }
15: }

使用了附加的new修飾符,編譯器就知道你重定義了基類的方法,它應該屏蔽基類方法。但是,如果你按以下方式編寫:
DerivedClass test = new DerivedClass();
((BaseClass)test).TestMethod();
  基類方法的實現就被調用了。這種行為不同於改寫方法,後者保證大部指派生方法




相關文章

Cloud Intelligence Leading the Digital Future

Alibaba Cloud ACtivate Online Conference, Nov. 20th & 21st, 2019 (UTC+08)

Register Now >

Starter Package

SSD Cloud server and data transfer for only $2.50 a month

Get Started >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

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

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