在前一節我們介紹了如何組織應用程式結構,本文將透過一個綜合範例來實際演練。這個小遊戲雖然功能簡單,卻涵蓋了命名空間劃分、多類別定義、動態連結庫(DLL)跨檔案呼叫、例外處理、類別繼承、虛擬方法覆寫、多型實作等核心概念。建議讀者細讀程式碼,加深對基本觀念的理解。
程式是一個小型圖形遊戲:系統隨機產生矩形、正方形、直角三角形、等腰直角三角形四種圖形。玩家初始擁有 1000 分,每回合自行下注,根據下注額與隨機圖形面積計算贏得分數。程式使用 System.Random 類別產生隨機數。
整個專案分為五個原始檔,分別編譯為兩個 DLL 及一個執行檔:
Shape,包含虛擬方法 Draw() 與 GetArea()。Shape 實作 Rectangle(矩形)與 Square(正方形),覆寫面積計算與繪製邏輯。Triangle(一般三角形)做為基底,再衍生 RectTriangle(直角三角形)與 RectEqualTriangle(等腰直角三角形),展示多層繼承與 new 關鍵字隱藏基底欄位。sp.Draw() 與 sp.GetArea()。// MyShape.cs
namespace MyShape {
public class Shape { ... }
public class Rectangle : Shape { ... }
public class Square : Rectangle { ... }
}
將相關類別歸入同一命名空間 MyShape,編譯為 MyShape.dll;訊息類別獨立為 MyMessage.dll。主程式透過 using MyShape; using MyMessage; 導入,達到模組化與重複使用。
public class Shape {
public virtual void Draw() { }
public virtual int GetArea() { return 0; }
}
public class Rectangle : Shape {
public override int GetArea() { return a * b; }
public override void Draw() { /* 繪製矩形 */ }
}
public class Square : Rectangle {
public Square(int va) : base(va, va) { }
public override void Draw() { /* 繪製正方形 */ }
}
virtual 允許衍生類別覆寫。override 實現執行期繫結,主程式宣告 Shape sp 卻能呼叫實際物件的 Draw() 與 GetArea(),即為多型。Square 繼承 Rectangle 並透過 base(va, va) 呼叫基底建構函式,展現建構函式鏈結。
try {
bet = int.Parse(s);
} catch {
bet = 100; // 輸入非數字預設押注 100
}
if (bet < score) score -= bet;
else { bet = score; score = 0; }
使用 try-catch 捕捉格式錯誤,避免程式當機;並檢查押注不得超過剩餘分數。
csc /target:library /out:MyShape.dll MyShape.cs rect.cs triangle.cs
csc /target:library /out:MyMessage.dll MyMessage.cs
csc /reference:MyShape.dll;MyMessage.dll client.cs
/target:library 產出 DLL。/reference 讓主程式參考兩個 DLL。// MyShape.cs
using System;
namespace MyShape {
public class Shape {
public virtual void Draw() { }
public virtual int GetArea() { return 0; }
}
}
// rect.cs
using System;
namespace MyShape {
public class Rectangle : Shape {
protected int a, b;
public Rectangle(int va, int vb) { a = va; b = vb; }
public override int GetArea() { return a * b; }
public override void Draw() {
Console.WriteLine("Rectangle:");
Console.WriteLine("* * * * *");
Console.WriteLine("* *");
Console.WriteLine("* *");
Console.WriteLine("* * * * *");
}
}
public class Square : Rectangle {
public Square(int va) : base(va, va) { }
public override void Draw() {
Console.WriteLine("Square");
Console.WriteLine("* * * * *");
Console.WriteLine("* *");
Console.WriteLine("* *");
Console.WriteLine("* *");
Console.WriteLine("* * * * *");
}
}
}
// triangle.cs
using System;
namespace MyShape {
public class Triangle : Shape {
protected int a, b, c;
public Triangle(int va, int vb, int vc) { a = va; b = vb; c = vc; }
public override int GetArea() {
int s = (a + b + c) / 2;
return (int)Math.Sqrt(s * (s - a) * (s - b) * (s - c));
}
}
public class RectTriangle : Triangle {
new protected int a, b;
public RectTriangle(int va, int vb) : base(va, vb, (int)Math.Sqrt(va * va + vb * vb)) {
a = va; b = vb;
}
public override int GetArea() { return a * b / 2; }
public override void Draw() {
Console.WriteLine("RectTriangle");
Console.WriteLine("*");
Console.WriteLine("* *");
Console.WriteLine("* *");
Console.WriteLine("* * * *");
}
}
public class RectEqualTriangle : RectTriangle {
new protected int a;
public RectEqualTriangle(int va) : base(va, va) { a = va; }
public override int GetArea() { return a * a / 2; }
public override void Draw() {
Console.WriteLine("RectEqualTriangle");
Console.WriteLine("*");
Console.WriteLine("* *");
Console.WriteLine("* *");
Console.WriteLine("* *");
Console.WriteLine("* * * * *");
}
}
}
// Mymessage.cs
using System;
namespace MyMessage {
public class Message {
public void Begin() {
Console.WriteLine("******************************");
Console.WriteLine("* SHAPE GAME *");
Console.WriteLine("******************************");
}
public bool Ask() {
Console.WriteLine("Press 0 to exit, any other key to continue");
return Console.Read() != '0';
}
}
}
// client.cs
using System;
using MyShape;
using MyMessage;
class ClientTest {
public static void Main() {
int score = 1000, win, choice, bet;
string s;
Shape sp = new Shape();
Random ran = new Random();
Message msg = new Message();
msg.Begin();
while (true) {
if (!msg.Ask()) break;
Console.WriteLine("Your Score: {0}", score);
Console.WriteLine("Enter your bet:");
s = Console.ReadLine();
try { bet = int.Parse(s); } catch { bet = 100; }
if (bet < score) score -= bet;
else { bet = score; score = 0; }
Console.WriteLine("Remain Score: {0}", score);
win = 0;
for (int i = 0; i < 3; i++) {
choice = ran.Next() % 4;
switch (choice) {
case 0: sp = new RectTriangle(5, 4); break;
case 1: sp = new RectEqualTriangle(5); break;
case 2: sp = new Rectangle(5, 4); break;
case 3: sp = new Square(5); break;
}
sp.Draw();
win += sp.GetArea() * (i + 1) * bet / 100;
Console.WriteLine("Your win: {0}", win);
}
score += win;
Console.WriteLine("Your Score: {0}", score);
if (score < 100) {
Console.WriteLine("Score too low to continue.");
break;
}
}
}
}
此範例完整展示了 C# 專案模組化的實務做法:以命名空間劃分邊界、將功能編譯為獨立 DLL、主程式參考 DLL、善用繼承與多型簡化主流程、例外處理提升健壯性。讀者可嘗試擴充新圖形、加入記分板或圖形化介面,進一步體會架構組織的彈性與維護優勢。