C#基础08-面向对象

零、文章目录

C#基础08-面向对象

1、面向对象三大特性

(1)封装(Encapsulation)
  • 核心思想:隐藏对象内部细节,仅暴露必要接口,通过访问修饰符控制数据安全。
  • 访问修饰符
    • private:仅本类内可访问(默认修饰符)
    • protected:本类及子类可访问
    • internal:同一程序集内可访问
    • public:全局可访问
    • protected internalprotected + internal 权限之和
  • 实现方式
    • 字段封装:私有字段 + 公共属性(如 private string _name; public string Name { get; set; }
    • 方法封装:仅公开必要方法,隐藏内部逻辑(如数据验证)
csharp 复制代码
public class BankAccount {
    private double _balance;  // 私有字段 
    public void Deposit(double amount) {  // 公开方法 
        if (amount > 0) _balance += amount;
    }
}
  • 设计原则
    • 单一职责原则(SRP):一个类只负责一项功能
    • 开闭原则(OCP):对扩展开放,对修改关闭
(2)继承(Inheritance)
  • 核心思想:子类复用父类特性,并扩展新功能。
  • 关键规则
    • 单继承:C# 仅支持单继承(如 class Dog : Animal
    • 里氏替换原则:子类对象可替代父类对象(如 Animal animal = new Dog();
    • 构造方法顺序:先执行父类构造方法,再执行子类
    • 成员继承:子类继承父类所有成员(包括私有成员,但不可直接访问)
  • 实现方式
    • 类继承:子类继承父类属性和方法
    • 抽象类与接口
      • 抽象类(abstract):包含未实现方法,需子类重写
      • 接口(interface):纯契约定义,无实现
csharp 复制代码
public class Animal {
    public virtual void Speak() => Console.WriteLine("Animal sound");
}
public class Dog : Animal {
    public override void Speak() => Console.WriteLine("Bark!");  // 重写父类方法
}
  • 禁用继承:用 sealed 修饰类(如 sealed class FinalClass
(3)多态(Polymorphism)
  • 核心思想:同一操作作用于不同对象时,表现出不同行为。
  • 编译时多态(静态)
    • 方法重载:同名方法不同参数(参数类型/数量不同)
csharp 复制代码
public class Calculator {
    public int Add(int a, int b) => a + b;
    public double Add(double a, double b) => a + b;  // 重载
}
复制代码
- 方法隐藏:子类用 `new` 隐藏父类方法(非重写)  
csharp 复制代码
public class Parent { public void Print() => Console.WriteLine("Parent"); }
public class Child : Parent { 
    public new void Print() => Console.WriteLine("Child");  // 隐藏父类方法
}
  • 运行时多态(动态)
    • 虚方法重写(virtual + override):
csharp 复制代码
Animal animal = new Dog();
animal.Speak();  // 输出 "Bark!"(运行时根据实际对象类型决定)
复制代码
- 抽象方法重写(`abstract`):父类声明抽象方法,子类强制实现  
- 接口多态:通过接口引用调用不同实现  
csharp 复制代码
interface IDrawable { void Draw(); }
class Circle : IDrawable { public void Draw() => Console.WriteLine("Drawing circle"); }
class Square : IDrawable { public void Draw() => Console.WriteLine("Drawing square"); }

IDrawable shape = new Circle();
shape.Draw();  // 输出 "Drawing circle"
  • 多态优势
    • 提高代码扩展性:新增子类无需修改父类逻辑
    • 增强灵活性:同一接口处理不同对象(如 List<Animal> 存储多种动物)
(4)三大特性对比表
特性 核心目的 关键实现 设计原则
封装 数据安全与简化使用 访问修饰符 + 属性/方法封装 单一职责原则 (SRP)
继承 代码复用与层次化设计 类继承 (:) + 抽象类/接口 里氏替换原则 (LSP)
多态 接口统一与行为多样化 重载/重写 (override)/接口实现 依赖倒置原则 (DIP)
(5)实战建议
  • 封装场景:敏感数据(如密码)设为 private,通过公共方法访问。
  • 继承陷阱:避免过度继承导致"脆弱基类问题",优先使用组合。
  • 多态优化:
    • virtual/override 替代 new 实现真正多态
    • 接口多态适用于跨组件协作(如插件系统)
  • SOLID 补充:结合开闭原则(OCP)和接口隔离原则(ISP)提升设计质量。

2、访问修饰符

(1)访问范围从宽到严
  • public
    • 权限:无限制访问
    • 适用对象:类、成员(字段/方法/属性)
    • 场景:跨程序集公开API
csharp 复制代码
public class Logger { public string LogPath; } // 任意程序集可访问
  • protected internal
    • 权限:protected + internal 的并集(当前程序集 或 任意派生类)
    • 场景:组件库中供派生类扩展的接口
csharp 复制代码
protected internal void Initialize() { } // 当前程序集或子类可用 
  • internal
    • 权限:仅当前程序集内可访问(默认类访问级别)
    • 场景:模块内部实现隐藏
csharp 复制代码
internal class Cache { } // 仅本项目可见 
  • protected
    • 权限:当前类 及派生类(不限程序集)
    • 场景:设计可继承框架
csharp 复制代码
protected virtual void OnStart() { } // 子类可重写 
  • private
    • 权限:仅当前类内部(成员默认访问级别)
    • 场景:封装内部状态
csharp 复制代码
private string _connectionKey; // 仅本类访问 
(2)关键规则与限制
规则类型 说明 示例
类修饰符限制 类仅支持 publicinternal(默认internal internal class Utility → 禁止跨程序集实例化
成员可访问性 成员不能比其所属类更开放(如 internal 类中不能声明 public 成员) internal class A { public void Foo() } → 编译错误
接口/显式实现 接口成员或显式接口实现禁止使用访问修饰符 interface IDevice { void Run(); } → 隐含 public
跨程序集继承 派生类访问基类protected成员时,需遵守基类程序集的internal限制 基类internal成员 → 即使protected,外部程序集派生类仍不可访问
(3)权限范围对比表
修饰符 当前类 同程序集派生类 同程序集非派生类 跨程序集派生类 跨程序集非派生类
public ✔️ ✔️ ✔️ ✔️ ✔️
protected internal ✔️ ✔️ ✔️ ✔️
internal ✔️ ✔️ ✔️
protected ✔️ ✔️ ✔️
private ✔️
  • 设计原则:
    • 优先用 private 封装细节,通过公共方法暴露功能
    • 库开发时,用 internal 隐藏非公开API,protected 预留扩展点
    • 避免滥用 public(增大耦合风险)和 protected internal(增加理解成本)
(4)常见误区与陷阱
  • 默认权限混淆
    • 类默认 internal,成员默认 private → 未显式声明时易导致访问错误
  • 跨程序集保护失效
csharp 复制代码
// 程序集A
public class Base { protected int Key; } 
// 程序集B(引用A)
class Derived : Base { 
    void UseKey() { Console.Write(Key); } // ✅ 可访问 
    static void LeakKey(Base b) { 
        Console.Write(b.Key); // ❌ 编译错误:跨程序集仅能通过派生类实例访问
    }
}
  • private protected(C# 7.2+)
    • 限制比 protected internal 更严:仅允许 同一程序集中的派生类
csharp 复制代码
private protected string _secret; // 当前类或同程序集派生类可访问
(5)总结与最佳实践
  • 安全优先:成员起始设为 private,按需放宽权限
  • 组件设计:
    • public → 稳定接口
    • internal → 内部实现
    • protected → 扩展点
  • 代码审查:检查所有 public 成员是否必要,减少全局暴露

3、继承

(1)基础概念
  • 定义与作用
    • 继承:子类(派生类)基于父类(基类)创建,自动获得父类非私有成员(字段/方法/属性)
    • 目的:实现代码复用、逻辑分层(如将通用字段 ID/CreateTime 提取至基类)
    • 特性:
      • C# 仅支持单继承(一个子类只能有一个直接父类)
      • 未指定父类时,默认继承 System.Object
  • 语法结构
csharp 复制代码
class 父类 { /* 成员定义 */ }  
class 子类 : 父类 { /* 新增或重写成员 */ }  // 继承声明 
(2)关键特性与规则
  • 构造函数执行顺序:
    • 子类实例化时按以下顺序执行构造函数
    • 若无显式构造函数,系统自动生成无参构造方法
  • 成员访问控制
关键字 子类访问权限 示例
public ✅ 可直接访问 子类.字段名
protected ✅ 可直接访问 子类.字段名
private ❌ 不可访问 需通过父类公共方法间接访问
internal 同程序集内可访问 跨类但同项目
  • 方法重写与隐藏:重写影响多态行为;隐藏仅覆盖当前子类。
方式 关键字 特点 示例
重写 override 修改父类虚方法逻辑(需父类方法标记 virtual public override void Method()
隐藏 new 创建与父类同名的新方法,隐藏继承的原始方法 public new void Method()
(3)进阶特性与注意事项
  • 多层继承传递性:若 C → B → A,则 C 继承 BA 的所有非私有成员
csharp 复制代码
class Animal {}  
class Mammal : Animal {}  // 继承Animal  
class Dog : Mammal {}     // 继承Mammal(间接继承Animal)  
  • 避免多重继承歧义:C# 不支持多父类继承(如 class C : A, B 会报错),需通过接口实现多重能力。
  • 装箱与拆箱影响:继承链中值类型与引用类型转换可能引发性能损耗(如 object obj = new Dog();)。
(4)实战示例
  • 场景:手机类继承体系
csharp 复制代码
// 父类:基础手机属性 
class Phone {
    public string Color { get; set; }
    public double Price { get; set; }
    public Phone(string color, double price) {
        Color = color; 
        Price = price;
    }
}
 
// 子类:扩展电池属性 
class CellPhone : Phone {
    public string MaxBatteryLife { get; set; }
    
    // 调用父类构造 + 子类扩展 
    public CellPhone(string color, double price, string battery) 
        : base(color, price)  // 调用父类构造函数 
    {
        MaxBatteryLife = battery;
    }
}
 
// 使用
var myPhone = new CellPhone("Black", 3999.99, "48小时");
Console.WriteLine($"颜色:{myPhone.Color}, 续航:{myPhone.MaxBatteryLife}");
  • 关键点:
    • 子类通过 : base(...) 传递参数至父类构造器
    • 子类直接使用父类属性 Color/Price
(5)常见错误及规避
  • 未调用父类构造导致初始化失败
csharp 复制代码
class Child : Parent {
    public Child() { }  // 错误!未显式调用父类构造
    // 修正:public Child() : base() { }
}
  • 混淆重写与隐藏:多态场景下,new 隐藏方法不会覆盖父类引用调用结果
csharp 复制代码
Parent obj = new Child();
obj.Method();  // 若Child.Method用new隐藏,此处仍调用Parent.Method 
(6)总结建议
  • 优先组合而非继承:过度继承易导致层级复杂,推荐用组合(类成员引用其他类)替代。
  • 合理使用 sealed:对无需再派生的类标记 sealed 阻止继承,提升安全性。
  • 实战练习:对比 struct(值类型无继承)与 class 的适用场景。

4、接口

(1)本质与核心特性
  • 契约式设计
    • 接口定义类或结构体必须实现的方法、属性、事件或索引器签名,但不含具体实现。
    • 示例:
csharp 复制代码
public interface ILogger {
    void Log(string message);  // 方法签名
    string LogLevel { get; set; }  // 属性签名 
}
  • 关键特性
    • 多重继承:一个类可实现多个接口,突破 C# 单继承限制。
    • 解耦与标准化:强迫实现类遵循统一规范,提升代码可维护性。
    • 默认公开性:接口成员隐式为 public,不可添加访问修饰符。
(2)核心应用场景
  • 实现多态与灵活替换
csharp 复制代码
IAnimal animal = new Dog();  // 多态引用
animal.Speak();              // 输出 "Woof!"
animal = new Cat();          // 动态替换实现类
animal.Speak();              // 输出 "Meow"
  • 支持设计模式
    • 策略模式:通过接口封装算法族,运行时切换策略
csharp 复制代码
public interface ISortStrategy { void Sort(List<int> data); }
public class QuickSort : ISortStrategy { /* 实现 */ }
public class Context { 
    private ISortStrategy _strategy;
    public void SetStrategy(ISortStrategy strategy) => _strategy = strategy;
} 
复制代码
- 工厂模式:接口定义创建契约,子类决定实例化逻辑。
  • 组件化开发:定义数据访问层接口 IDataAccess,支持切换不同数据库实现:
csharp 复制代码
public class MongoDBService : IDataAccess { /* MongoDB 实现 */ }
public class SqlService : IDataAccess { /* SQL Server 实现 */ } 
  • 跨模块通信:事件驱动架构中,通过接口标准化事件处理:
csharp 复制代码
public interface IEventHandler {
    void Handle(Event e);
}
(3)高级特性与注意事项
  • 显式接口实现:解决同名方法冲突,通过接口名限定调用:
csharp 复制代码
void IInterfaceA.Method() { }  // 显式实现
  • 接口继承链:接口可多重继承其他接口,合并契约:
csharp 复制代码
public interface IComboBox : ITextBox, IListBox { } 
  • 泛型接口:增强类型安全性与复用性:
csharp 复制代码
public interface IRepository<T> {
    T GetById(int id);
} 
  • 使用限制
    • 不可包含字段/构造函数:仅定义行为契约。
    • 默认方法(C# 8.0+):允许接口提供默认实现,减少破坏性升级。
(4)最佳实践总结
  • 命名规范:接口名以 I 开头(如 IEnumerable)。
  • 单一职责:每个接口聚焦单一功能(如 ISaveable 而非 IFileOperations)。
  • 接口隔离:避免"胖接口",按需拆分为小接口。
  • 依赖倒置:高层模块依赖抽象接口,而非具体实现。

5、抽象类

(1)基础概念
  • 定义与特性
    • 使用 abstract 关键字声明抽象类,如:
csharp 复制代码
public abstract class Animal  // 抽象类 
{
    public abstract void MakeSound();  // 抽象方法(无实现)
    public void Sleep()                // 非抽象方法(有默认实现)
    {
        Console.WriteLine("Sleeping...");
    }
}
复制代码
- 核心特性:  
    * ❗ 不可实例化:不能通过 `new` 创建对象(如 `new Animal()` 非法)。  
    * ✅ 可包含混合成员:可同时有抽象方法(无方法体)和非抽象方法(有实现)。  
    * 🔒 强制子类实现:非抽象子类必须重写所有抽象方法(否则编译错误)。
  • 设计目的
    • 为相关类提供共性模板(如动物都有 MakeSound 行为),具体实现由子类完成。
    • 解决类族中 "部分行为需自定义,部分行为可复用" 的问题。
️(2)核心规则
  • 抽象方法约束
    • 抽象方法只能存在于抽象类中,且无方法体(以分号结尾):
csharp 复制代码
public abstract void Eat();  // 仅声明,无实现 
复制代码
- 子类必须通过 `override` 实现:
csharp 复制代码
public class Dog : Animal 
{
    public override void MakeSound() => Console.WriteLine("Woof!");
}
  • 非抽象成员支持
    • 抽象类可包含字段、属性、构造函数和具体方法:
csharp 复制代码
public abstract class Fruit 
{
    public string Color { get; set; }  // 具体属性
    public abstract float Price { get; } // 抽象属性
}
  • 构造与继承机制
    • 抽象类可以有构造函数(通常用于初始化公共字段)。
    • 支持多层继承(如 Animal → Mammal → Dog)。
(3)抽象类 vs 接口
特性 抽象类 (abstract class) 接口 (interface)
实现继承 ✅ 单继承 ✅ 多实现
方法实现 ✅ 可含具体方法 ❌ 仅方法签名
字段/属性 ✅ 可包含字段和属性初始值 ❌ 仅自动属性(无字段)
设计目标 表征 IS-A 关系(如狗是动物) 表征 CAN-DO 能力(如可飞行)
  • 决策建议:
    • 需多态且无关类共享行为 → 接口(如日志服务 ILogger)。
    • 存在通用代码需复用 → 抽象类(如游戏实体 GameEntity 基类)。
(4)典型应用场景
  • 框架设计(如游戏角色系统):
csharp 复制代码
public abstract class Character {
    public int Health { get; set; }
    public abstract void Attack();  // 攻击方式由子类定义 
    public void TakeDamage(int damage) => Health -= damage; // 通用逻辑
}
public class Warrior : Character {
    public override void Attack() => Console.WriteLine("Sword slash!");
}
  • 强制规范工具类
csharp 复制代码
public abstract class Logger {
    public abstract void Log(string message);  // 子类必须实现日志写入方式
    public void LogError(string error) => Log($"[ERROR] {error}"); // 通用封装 
}
️(5)关键注意事项
  • 禁止密封:抽象类不可用 sealed 修饰(否则无法继承)。
  • 构造函数用途:仅用于子类初始化(如 public Dog() : base("Mammal"))。
  • 多态性支持:可通过基类引用调用子类实现(如 Animal a = new Dog(); a.MakeSound();)。

6、方法重写

(1)基础概念
  • 定义与目的
    • 重写(Override):派生类修改基类中声明为virtualabstract的方法实现,实现多态性。
    • 核心价值:
      • 扩展基类功能,支持差异化行为(如不同动物的叫声)。
      • 符合开闭原则(对扩展开放,对修改关闭)。
  • 语法规则
角色 关键字 示例
基类方法 virtual public virtual void Speak() { ... }
派生类方法 override public override void Speak() { ... }
  • 关键限制:
    • 静态方法、私有方法(private)不可重写;
    • 重写方法必须与基类方法完全一致(名称、参数、返回值)。
(2)核心机制
  • 多态性实现:通过基类引用调用方法,运行时自动绑定派生类实现(动态绑定)。
csharp 复制代码
Animal dog = new Dog();  
dog.Speak(); // 输出"狗叫:汪汪!"(实际类型决定行为)  
  • 调用基类实现:使用base关键字访问被重写的基类方法:
csharp 复制代码
public class Car : Vehicle {  
    public override void Start() {  
        base.Start(); // 先执行基类启动逻辑  
        Console.WriteLine("引擎点火"); // 扩展行为  
    }  
} 
  • 抽象方法重写:抽象类中声明abstract方法,强制派生类重写:
csharp 复制代码
public abstract class Shape {  
    public abstract void Draw(); // 无实现  
}  
public class Circle : Shape {  
    public override void Draw() => Console.WriteLine("绘制圆形"); // 必须实现  
} 
(3)重写 vs 隐藏
特性 重写(override) 隐藏(new)
基类要求 virtual/abstract方法 任意方法(无需virtual
多态行为 运行时按实际类型调用派生类方法 编译时按声明类型调用
调用结果 Animal obj = new Dog(); obj.Speak() → 调用Dog.Speak() Animal obj = new Dog(); obj.Speak() → 调用Animal.Speak()
  • 优先使用重写:确保多态性,避免隐藏导致的逻辑混淆。
️(4)实战应用场景
  • 定制化行为(如不同动物叫声)
csharp 复制代码
public class Cat : Animal {  
    public override void Speak() => Console.WriteLine("猫叫:喵喵!");  
}  
  • 扩展基类功能(如车辆启动流程增强)
csharp 复制代码
public class ElectricCar : Car {  
    public override void Start() {  
        base.Start(); // 保留基类启动逻辑  
        Console.WriteLine("电池系统激活"); // 新增步骤  
    }  
} 
  • 实现设计模式(如模板方法模式)
csharp 复制代码
public abstract class GameAI {  
    public void Turn() {  
        CollectResources();  
        BuildStructures(); // 可重写的步骤  
    }  
    protected virtual void BuildStructures() { } // 默认空实现  
}  
public class OrcAI : GameAI {  
    protected override void BuildStructures() => Console.WriteLine("建造兽人兵营");  
} 
(5)关键注意事项
  • 访问权限:重写方法访问级别需与基类一致(如public重写public)。
  • 异常处理:重写方法抛出的异常类型必须与基类相同或是其子类。
  • 性能优化:避免深度继承链中频繁重写,可能导致调用栈过深。
  • 调试技巧:使用base调试基类逻辑,用this验证派生类行为,结合断点观察多态调用链。

7、命名空间

(1)核心作用
  • 解决命名冲突
    • 将全局作用域划分为独立区块,避免不同库中同名类型/函数冲突。
    • 使用时通过完整限定名区分:CompanyA.Project.Logger vs CompanyB.Project.Logger
csharp 复制代码
namespace CompanyA.Project { class Logger { } }
namespace CompanyB.Project { class Logger { } }
  • 代码组织与封装
    • 类似文件系统的文件夹结构,将相关类型分组管理(如System.IO处理输入输出)。
    • 支持嵌套命名空间(如System.Collections.Generic),实现逻辑分层。
(2)语法与使用方式
  • 定义命名空间
csharp 复制代码
namespace MyApplication.Data {
    class Database { }  // 实际类型名为 MyApplication.Data.Database
}
  • 引用命名空间
    • 完全限定名:直接使用完整路径(System.Console.WriteLine())。
    • using指令:简化代码,避免重复书写命名空间:
csharp 复制代码
using System;
Console.WriteLine("Hello");  // 无需写 System.Console 
复制代码
- 别名(Alias):解决同名冲突或简化长命名空间:  
csharp 复制代码
using ProjectA = CompanyA.Project;
ProjectA.Logger.Log();  // 调用 CompanyA.Project.Logger 
(3)实际应用场景
  • 类库开发
    • 第三方库(如NuGet包)必须使用独立命名空间,防止污染用户全局作用域。
    • 示例:ST硬件驱动库通过命名空间封装外设寄存器组,提高可读性。
  • 大型项目分层
    • 推荐结构:
csharp 复制代码
namespace App.BusinessLogic;  // 业务逻辑层
namespace App.DataAccess;     // 数据访问层 
namespace App.WebUI;          // 表示层
复制代码
- 分层后,各模块通过`using`按需引用,减少耦合。
  • 与物理文件的关系
    • 命名空间可跨多个文件定义(非连续),例如:
      • File1.cs: namespace MyLib { class A { } }
      • File2.cs: namespace MyLib { class B { } }
        编译器自动合并为同一命名空间。
(4)最佳实践与注意事项
  • 避免全局污染
    • 慎用using namespace *;(如using namespace System),尤其在头文件中,易引发冲突。
    • 优先使用局部using或完全限定名。
  • 命名规范
    • 采用公司/项目名作为根命名空间(如Microsoft.Azure)。
    • 层次清晰:<Company>.<Product>.<Module>
  • 未命名命名空间:用于文件内私有类型(等效于internal访问修饰符),限制外部访问:
csharp 复制代码
namespace { 
    class InternalHelper { }  // 仅在当前程序集可见 
}
相关推荐
李宥小哥2 小时前
C#基础07-类与对象
服务器·数据库·c#
nsjqj2 小时前
数据结构:Map 和 Set (二)
java·开发语言·数据结构
pixelpilot3 小时前
Nimble:让SwiftObjective-C测试变得更优雅的匹配库
开发语言·其他·objective-c·swift
froginwe113 小时前
C# 循环
开发语言
EnCi Zheng3 小时前
Java_钻石操作符详解
java·开发语言
拾忆,想起4 小时前
RabbitMQ事务机制深度剖析:消息零丢失的终极武器
java·开发语言·分布式·后端·rabbitmq·ruby
IvanCodes4 小时前
八、Scala 集合与函数式编程
大数据·开发语言·scala
包达叔4 小时前
仿NewLife的XmlConfig类实现Json配置文件
c#·json·newlife
Never_Satisfied5 小时前
在JavaScript / HTML中,浏览器提示 “Refused to execute inline event handler” 错误
开发语言·javascript·html