C# 面向對象編程實例演示
一、基礎概念回顧
面向對象編程(OOP)的四大基本特性:
- ??封裝?? - 將數據和操作數據的方法綁定在一起
- ??繼承?? - 創建新類時重用現有類的屬性和方法
- ??多態?? - 同一操作作用于不同對象產生不同結果
- ??抽象?? - 簡化復雜系統,只暴露必要接口
二、完整實例演示
示例1:銀行賬戶系統
using System;// 抽象基類 - 封裝共同屬性和行為
public abstract class BankAccount
{// 封裝字段private string _accountNumber;private decimal _balance;// 屬性 - 控制對字段的訪問public string AccountNumber { get => _accountNumber; private set => _accountNumber = value; }public decimal Balance { get => _balance; protected set => _balance = value; }// 構造函數protected BankAccount(string accountNumber, decimal initialBalance){AccountNumber = accountNumber;Balance = initialBalance;}// 抽象方法 - 由子類實現public abstract void Deposit(decimal amount);public abstract void Withdraw(decimal amount);// 虛方法 - 可被子類重寫public virtual void DisplayAccountInfo(){Console.WriteLine($"賬戶號: {AccountNumber}");Console.WriteLine($"余額: {Balance:C}");}
}// 派生類 - 繼承并擴展功能
public class SavingsAccount : BankAccount
{private const decimal MinimumBalance = 100;private const decimal InterestRate = 0.02m;public SavingsAccount(string accountNumber, decimal initialBalance) : base(accountNumber, initialBalance){if (initialBalance < MinimumBalance)throw new ArgumentException("儲蓄賬戶最低余額為100元");}// 實現抽象方法public override void Deposit(decimal amount){if (amount <= 0)throw new ArgumentException("存款金額必須大于0");Balance += amount;Console.WriteLine($"成功存入 {amount:C},當前余額: {Balance:C}");}public override void Withdraw(decimal amount){if (amount <= 0)throw new ArgumentException("取款金額必須大于0");if (Balance - amount < MinimumBalance)throw new InvalidOperationException("取款后余額不能低于最低限額");Balance -= amount;Console.WriteLine($"成功取出 {amount:C},當前余額: {Balance:C}");}// 新增方法public void ApplyInterest(){decimal interest = Balance * InterestRate;Balance += interest;Console.WriteLine($"利息已應用: {interest:C},新余額: {Balance:C}");}// 重寫虛方法public override void DisplayAccountInfo(){base.DisplayAccountInfo();Console.WriteLine($"賬戶類型: 儲蓄賬戶");Console.WriteLine($"最低余額要求: {MinimumBalance:C}");}
}// 另一個派生類
public class CheckingAccount : BankAccount
{private const decimal OverdraftLimit = -500;public CheckingAccount(string accountNumber, decimal initialBalance) : base(accountNumber, initialBalance){}public override void Deposit(decimal amount){if (amount <= 0)throw new ArgumentException("存款金額必須大于0");Balance += amount;Console.WriteLine($"支票賬戶存入 {amount:C},當前余額: {Balance:C}");}public override void Withdraw(decimal amount){if (amount <= 0)throw new ArgumentException("取款金額必須大于0");if (Balance - amount < OverdraftLimit)throw new InvalidOperationException($"取款后余額不能低于透支限額 {OverdraftLimit:C}");Balance -= amount;Console.WriteLine($"支票賬戶取出 {amount:C},當前余額: {Balance:C}");}public void WriteCheck(decimal amount){try{Withdraw(amount);Console.WriteLine($"已開具 {amount:C} 支票");}catch (Exception ex){Console.WriteLine($"開具支票失敗: {ex.Message}");}}
}// 演示類
public class BankDemo
{public static void Main(){// 多態 - 使用基類引用指向派生類對象BankAccount savings = new SavingsAccount("SAV12345", 1000);BankAccount checking = new CheckingAccount("CHK67890", 500);// 調用方法savings.Deposit(200);savings.Withdraw(50);savings.DisplayAccountInfo();Console.WriteLine("\n-----------------\n");checking.Deposit(300);checking.WriteCheck(100);checking.Withdraw(700); // 嘗試透支checking.DisplayAccountInfo();// 使用is和as運算符if (savings is SavingsAccount savingsAcc){savingsAcc.ApplyInterest();}BankAccount? unknownAccount = null;var tempAccount = unknownAccount as SavingsAccount;if (tempAccount == null){Console.WriteLine("賬戶類型轉換失敗");}}
}
示例2:圖形計算系統
using System;
using System.Collections.Generic;// 接口定義
public interface IShape
{decimal CalculateArea();decimal CalculatePerimeter();void Draw();
}// 具體實現類
public class Circle : IShape
{private decimal _radius;public Circle(decimal radius){if (radius <= 0)throw new ArgumentException("半徑必須大于0");_radius = radius;}public decimal Radius => _radius;public decimal CalculateArea() => (decimal)Math.PI * _radius * _radius;public decimal CalculatePerimeter() => 2 * (decimal)Math.PI * _radius;public void Draw(){Console.WriteLine($"繪制圓形,半徑: {_radius}");// 實際繪圖代碼...}
}public class Rectangle : IShape
{private decimal _width;private decimal _height;public Rectangle(decimal width, decimal height){if (width <= 0 || height <= 0)throw new ArgumentException("寬度和高度必須大于0");_width = width;_height = height;}public decimal Width => _width;public decimal Height => _height;public decimal CalculateArea() => _width * _height;public decimal CalculatePerimeter() => 2 * (_width + _height);public void Draw(){Console.WriteLine($"繪制矩形,寬度: {_width},高度: {_height}");// 實際繪圖代碼...}
}// 工廠類 - 封裝對象創建邏輯
public static class ShapeFactory
{public static IShape CreateShape(string shapeType, params decimal[] parameters){return shapeType.ToLower() switch{"circle" => new Circle(parameters[0]),"rectangle" => new Rectangle(parameters[0], parameters[1]),_ => throw new ArgumentException("未知的形狀類型")};}
}// 圖形管理器 - 組合多個圖形
public class ShapeManager
{private readonly List<IShape> _shapes = new();public void AddShape(IShape shape){_shapes.Add(shape);}public decimal TotalArea() => _shapes.Sum(s => s.CalculateArea());public decimal TotalPerimeter() => _shapes.Sum(s => s.CalculatePerimeter());public void DrawAllShapes(){foreach (var shape in _shapes){shape.Draw();}}
}// 演示類
public class ShapeDemo
{public static void Main(){// 多態使用IShape circle = new Circle(5);IShape rectangle = new Rectangle(4, 6);Console.WriteLine($"圓形面積: {circle.CalculateArea()}");Console.WriteLine($"矩形周長: {rectangle.CalculatePerimeter()}");// 工廠模式使用IShape anotherCircle = ShapeFactory.CreateShape("circle", 3);IShape anotherRectangle = ShapeFactory.CreateShape("rectangle", 2, 5);// 組合模式使用var manager = new ShapeManager();manager.AddShape(circle);manager.AddShape(rectangle);manager.AddShape(anotherCircle);manager.AddShape(anotherRectangle);Console.WriteLine($"\n所有圖形總面積: {manager.TotalArea()}");Console.WriteLine($"所有圖形總周長: {manager.TotalPerimeter()}");Console.WriteLine("\n繪制所有圖形:");manager.DrawAllShapes();// 接口實現檢查if (circle is Circle c){Console.WriteLine($"這是一個圓形,半徑: {c.Radius}");}// 使用as運算符var maybeCircle = rectangle as Circle;if (maybeCircle == null){Console.WriteLine("這不是一個圓形");}}
}
示例3:動物模擬系統
using System;// 基類
public abstract class Animal
{// 字段封裝private string _name;private int _age;// 屬性public string Name { get => _name; set => _name = value ?? throw new ArgumentNullException(nameof(value)); }public int Age { get => _age; protected set => _age = value >= 0 ? value : throw new ArgumentOutOfRangeException(nameof(value)); }// 構造函數protected Animal(string name, int age){Name = name;Age = age;}// 抽象方法 - 必須由子類實現public abstract void MakeSound();// 虛方法 - 可被子類重寫public virtual void Eat(){Console.WriteLine($"{Name}正在吃東西");}// 密封方法 - 阻止進一步重寫public sealed override string ToString(){return $"{GetType().Name}: {Name}, {Age}歲";}
}// 派生類
public class Dog : Animal
{public Dog(string name, int age) : base(name, age){}public override void MakeSound(){Console.WriteLine($"{Name}汪汪叫!");}// 新增方法public void Fetch(){Console.WriteLine($"{Name}正在撿球");}
}public class Cat : Animal
{public Cat(string name, int age) : base(name, age){}public override void MakeSound(){Console.WriteLine($"{Name}喵喵叫!");}// 重寫基類方法public override void Eat(){Console.WriteLine($"{Name}正在優雅地吃貓糧");}// 隱藏基類方法(不推薦)public new void ToString(){Console.WriteLine("這不會覆蓋基類的ToString");}
}// 接口
public interface ITrainable
{void Train(string command);
}// 實現接口的類
public class PoliceDog : Dog, ITrainable
{public PoliceDog(string name, int age) : base(name, age){}public void Train(string command){Console.WriteLine($"{Name}正在接受'{command}'訓練");}
}// 演示類
public class AnimalDemo
{public static void Main(){// 多態使用Animal[] animals = new Animal[]{new Dog("旺財", 3),new Cat("咪咪", 2),new PoliceDog("阿黃", 5)};foreach (var animal in animals){animal.MakeSound();animal.Eat();// 類型檢查與轉換if (animal is Dog dog){dog.Fetch();}if (animal is ITrainable trainable){trainable.Train("坐下");}Console.WriteLine(animal.ToString());Console.WriteLine();}// 接口實現檢查if (animals[2] is PoliceDog policeDog){policeDog.Train("臥倒");}// as運算符var maybeTrainable = animals[1] as ITrainable;if (maybeTrainable == null){Console.WriteLine("貓不能接受訓練");}}
}
三、關鍵概念詳解
1. 封裝
??優點??:
- 隱藏實現細節
- 提供公共接口
- 控制對數據的訪問
??示例??:
public class Person
{// 私有字段private string _ssn;// 公共屬性 - 控制訪問public string SSN{get => _ssn;set{if (string.IsNullOrEmpty(value))throw new ArgumentException("SSN不能為空");if (value.Length != 9)throw new ArgumentException("SSN必須是9位");_ssn = value;}}// 只讀屬性public int Age { get; private set; }// 構造函數初始化public Person(string ssn, int age){SSN = ssn;Age = age;}// 方法封裝行為public void HaveBirthday(){Age++;Console.WriteLine($"生日快樂!現在是{Age}歲");}
}
2. 繼承
??語法??:
public class BaseClass
{// 基類成員
}public class DerivedClass : BaseClass
{// 派生類成員
}
??示例??:
public class Vehicle
{public string Make { get; set; }public string Model { get; set; }public virtual void Start(){Console.WriteLine("車輛啟動");}
}public class Car : Vehicle
{public int DoorCount { get; set; }public override void Start(){Console.WriteLine("汽車啟動");base.Start(); // 調用基類方法}public void Honk(){Console.WriteLine("喇叭響");}
}
3. 多態
??實現方式??:
- 方法重寫(override)
- 接口實現
- 抽象方法
??示例??:
public interface IShape
{decimal Area();
}public class Circle : IShape
{public decimal Radius { get; set; }public decimal Area() => (decimal)Math.PI * Radius * Radius;
}public class Rectangle : IShape
{public decimal Width { get; set; }public decimal Height { get; set; }public decimal Area() => Width * Height;
}// 使用
IShape[] shapes = new IShape[]
{new Circle { Radius = 5 },new Rectangle { Width = 4, Height = 6 }
};foreach (var shape in shapes)
{Console.WriteLine($"面積: {shape.Area()}");
}
4. 抽象
??抽象類??:
public abstract class Animal
{public abstract void MakeSound(); // 必須由子類實現public virtual void Eat() // 可選實現{Console.WriteLine("動物在吃東西");}
}
??接口??:
public interface IDriveable
{void Drive();int MaxSpeed { get; set; }
}
??示例??:
public abstract class Shape
{public abstract decimal CalculateArea();public virtual void Display(){Console.WriteLine("這是一個形狀");}
}public class Triangle : Shape
{public decimal Base { get; set; }public decimal Height { get; set; }public override decimal CalculateArea(){return Base * Height / 2;}public new void Display() // 隱藏基類方法(不推薦){Console.WriteLine("這是一個三角形");}
}
四、設計模式示例
1. 工廠模式
public interface IWeapon
{void Attack();
}public class Sword : IWeapon
{public void Attack() => Console.WriteLine("揮劍攻擊");
}public class Bow : IWeapon
{public void Attack() => Console.WriteLine("拉弓射箭");
}public static class WeaponFactory
{public static IWeapon CreateWeapon(string weaponType){return weaponType.ToLower() switch{"sword" => new Sword(),"bow" => new Bow(),_ => throw new ArgumentException("未知武器類型")};}
}// 使用
IWeapon weapon = WeaponFactory.CreateWeapon("sword");
weapon.Attack();
2. 單例模式
public sealed class Logger
{private static readonly Lazy<Logger> _instance = new Lazy<Logger>(() => new Logger());public static Logger Instance => _instance.Value;private Logger() { }public void Log(string message){Console.WriteLine($"[{DateTime.Now}] {message}");}
}// 使用
Logger.Instance.Log("系統啟動");
3. 觀察者模式
public interface IObserver
{void Update(string message);
}public class NewsAgency
{private readonly List<IObserver> _observers = new();public void AddObserver(IObserver observer){_observers.Add(observer);}public void RemoveObserver(IObserver observer){_observers.Remove(observer);}public void PublishNews(string news){foreach (var observer in _observers){observer.Update(news);}}
}public class Subscriber : IObserver
{public string Name { get; }public Subscriber(string name){Name = name;}public void Update(string message){Console.WriteLine($"{Name}收到新聞: {message}");}
}// 使用
var agency = new NewsAgency();
agency.AddObserver(new Subscriber("張三"));
agency.AddObserver(new Subscriber("李四"));agency.PublishNews("股市今日大漲");
五、最佳實踐
-
??優先使用組合而非繼承??:
// 更好的設計 - 使用組合 public class Engine { }public class Car {public Engine Engine { get; } = new Engine(); }
-
??遵循SOLID原則??:
- 單一職責原則(SRP)
- 開閉原則(OCP)
- 里氏替換原則(LSP)
- 接口隔離原則(ISP)
- 依賴倒置原則(DIP)
-
??合理使用訪問修飾符??:
public class MyClass {public int PublicField; // 慎用protected int ProtectedField;internal int InternalField;protected internal int ProtectedInternalField;private int _privateField; // 推薦 }
-
??避免過度使用繼承??:
- 繼承層次不要太深(通常不超過3層)
- 考慮使用接口或組合替代
-
??使用屬性而非公共字段??:
// 不推薦 public class BadDesign {public int Value; }// 推薦 public class GoodDesign {private int _value;public int Value{get => _value;set => _value = value > 0 ? value : throw new ArgumentOutOfRangeException();} }
-
??實現IDisposable接口管理資源??:
public class ResourceHolder : IDisposable {private bool _disposed = false;private FileStream _fileStream;public ResourceHolder(string path){_fileStream = new FileStream(path, FileMode.Open);}public void Dispose(){Dispose(true);GC.SuppressFinalize(this);}protected virtual void Dispose(bool disposing){if (!_disposed){if (disposing){_fileStream?.Dispose();}_disposed = true;}}~ResourceHolder() => Dispose(false); }
通過以上實例和最佳實踐,您可以更好地理解和應用C#的面向對象編程特性,編寫出更健壯、可維護和可擴展的代碼。