29.C#核心语法知识总结

29.总结


29.1 知识点

学习的主要内容

强调



29.2 核心要点速览

封装

类 和 对象

  • :模板/类型,定义特征(成员变量)、行为(成员方法)以及构造/析构等。
  • 对象:类的实例,引用类型,必须 new(或声明为 null)。

成员变量

  • 用途:描述对象特征。
  • 位置:类体内,任意类型,任意数量。
  • 默认值:值类型 = 0/false,引用类型 = null
  • 访问:由访问修饰符 (public/private/protected) 控制。

成员方法

  • 用途:描述对象行为。
  • 规则:写在类体内;不用 static;通过实例调用;可有任意参数和返回值。

构造 / 析构

  • 构造函数

    • 名称同类,无返回值,可重载。
    • 用于初始化成员;有参顶掉默认无参;可用 this(...) 链接重用。
  • 析构函数

    • ~ClassName(),无参数,无修饰符。
    • GC 回收对象时调用,C# 中少用。

C#垃圾回收机制

  1. 自动回收:对象无引用时,GC自动回收内存,无需手动释放。
  2. 分代策略
    • 第0代:新对象(优先回收)
    • 第1代:短期存活对象
    • 第2代:长期存活对象(如静态变量)
  3. 触发条件:内存不足、手动调用GC.Collect()或系统压力大时。
  4. 回收流程
    • 标记:从根对象遍历标记可达对象。
    • 清除:回收未标记对象。
    • 压缩:整理内存碎片。
  5. 优势:防内存泄漏,简化开发。
  6. 注意
    • 非托管资源(如文件句柄)需用usingDispose()手动释放。
    • 频繁GC影响性能,避免过度创建对象。

成员属性 (Properties)

  • 用途:包装字段,添加 get/set 逻辑,保护成员变量。

  • 语法

    public Type Name {
      get { … }  
      set { … }  // value 代表外部赋值
    }
    
  • 可选get/set 可单独存在;可加访问修饰符;支持自动属性:public int X { get; private set; }.

索引器 (Indexers)

  • 用途:让对象像数组一样用 obj[index] 访问内部集合。

  • 语法

    public ElementType this[int index] {
      get { … }  
      set { … }
    }
    
  • 可重载:参数数量/类型不同视为不同签名。

静态成员 & 静态类

  • 静态成员static 修饰字段、方法、属性等,程序启动即分配,用法:ClassName.Member
  • 静态类:只能包含静态成员,不能实例化,常作工具类。
  • 静态构造static ClassName() { … },无修饰符、无参数,仅调用一次,用于初始化静态成员。

扩展方法 (Extension Methods)

  • 作用:为现有类型“添砖加瓦”地增加方法,无需继承或修改原类型。

  • 要求:在静态类中写静态方法,首个参数加 this,指明要扩展的类型。

  • 语法

    public static void SpeakValue(this int value)
    {
    }
    

运算符重载

  • 关键词public static 返回类型 operator 符号(…)
  • 作用:让自定义类型支持 +-== 等操作符。
  • 注意:必须实现为静态方法;条件运算符需成对;不可使用 ref/out

内部类 & 分部类/分部方法

  • 内部类:在一个类内部定义另一个类,用于表示紧密关联的类型;访问时 Outer.Inner
  • 分部类partial class,同一类型可分散声明于多个文件,访问修饰符一致,成员不重复。
  • 分部方法partial void MethodName(); 与其实现分离,只能 void、无访问修饰符,局限性大。

继承

继承基本规则

  • 概念:子类可继承父类所有公共受保护成员(字段、方法、属性)。

  • 语法

    class Sub : Base { … }
    
  • 特点

    • 单根性:只能继承一个直接父类。
    • 传递性:可链式继承祖先类的成员。
  • 访问修饰符影响

    • public → 内外皆可访问
    • protected → 自身及子类可访问
    • private → 仅自身可访问
    • internal → 同程序集可访问
  • 示例

    class Teacher { protected int id; public void Speak(){} }
    class Lecturer : Teacher { public void Teach(){} }
    class SeniorLecturer : Lecturer { /* 可直接用 id/Speak/Teach */ }
    

继承中的构造函数

  • 执行顺序

    1. 最远祖先类(顶级父类)无参 / 指定构造
    2. 逐级向下:父类 → 子类 …
  • base 调用

    • 子类可在构造签名上显式调用父类某个构造:

      public Sub(int x) : base(x) { … }
      
    • 若不显式 base(...),则自动调用父类无参构造,故无参构造务必保留或补写

里氏替换原则 (LSP)

  • 核心:任何父类能出现的地方,子类都可替代。

  • 表现:可用父类类型的变量或容器存放子类实例。

  • 示例

    GameObject obj = new Player();
    GameObject[] list = { new Player(), new Monster(), new Boss() };
    

类型判断与转换 (is / as)

  • **is**:检查对象是否可视为某类型 → 返回 bool

  • **as**:尝试将对象转换为某类型 → 成功返回实例,失败返回 null

  • 示例

    if (obj is Player) 
       (obj as Player).Attack();
    

万物之父 (object)

  • 概念:所有类型(值与引用)的基类。

  • 用途:不确定类型时用作参数或容器。

  • 示例

    object o1 = 123;     // 装箱
    object o2 = "abc";   // 引用
    int x = (int)o1;     // 拆箱
    string s = o2 as string;
    

装箱与拆箱

  • 装箱:将值类型复制到堆上,生成引用类型实例。
  • 拆箱:将堆上已装箱的值类型提取回栈。
  • 性能:涉及堆→栈/栈→堆复制,频繁使用会有开销。

密封类 (sealed)

  • 概念:用 sealed 修饰的类,禁止被继承。

  • 语法

    sealed class MyClass { … }
    
  • 作用:确保设计不再被扩展,增强安全与规范。

多态

多态基本概念 (Polymorphism)

  • 定义:同一父类引用调用同名方法,执行子类不同实现
  • 关键词virtual(基类)、override(子类)、base(调用父级逻辑)
  • 好处:父类容器存放不同子类,调用统一接口即可实现不同表现

抽象类 & 抽象方法

  • 抽象类abstract class,不能实例化,可含抽象和虚方法
  • 抽象方法public abstract void M(); 无实现,子类必须 override
  • 虚方法public virtual void M(){} 可选覆写
  • 用途:在框架设计中定义“必须由子类实现”的接口模板

接口 (Interface)

  • 本质:纯“行为规范”声明,无字段,仅方法/属性/索引器/事件签名

  • 继承:类可继承多个接口;接口可继承接口

  • 实现:必须公开实现所有成员;支持显式实现解决同名冲突:

    void IFirst.M() {…}  void ISecond.M() {…}
    
  • 用途:解耦行为,与类继承并行实现多重职责

密封类 & 密封方法

  • 密封类sealed class C {} → 禁止任何派生
  • 密封方法public sealed override void M(){} → 在覆写后锁定,不再可重写
  • 用途:阻止不当继承或覆写,强化安全和稳定性

面向对象关联知识点

命名空间 (Namespace)

  • 作用:组织、分组、避免命名冲突,类似工具包

  • 声明

    namespace MyGame { class A { } }
    namespace MyGame.UI { class Image { } }
    
  • 引用

    using MyGame;
    var o = new GameObject();
    // 或显式指定全名
    var o2 = new MyGame.GameObject();
    
  • 同名类:不同命名空间可有同名类,必须 using + 命名空间或全名区分

  • 嵌套命名空间

    namespace MyGame { namespace UI { class Image { } } }
    using MyGame.UI;
    

object 类型核心成员

成员 功能
Equals(a,b) 静态:按左侧对象逻辑比较相等;实例:可重写自定义相等规则
ReferenceEquals 静态:判断引用是否相同(值类型永 false)
GetType() 返回运行时类型 Type,反射基础
MemberwiseClone 浅拷贝:值类型复制,引用类型仍指向同一对象
ToString() 虚:返回字符串,可重写定义格式;默认打印 Namespace.ClassName

string 核心方法

  • 不可变:每次变更都产生新实例 → 若频繁拼接用 StringBuilder
  • 索引访问s[i]
  • 查找IndexOf, LastIndexOf
  • 截取/移除Substring, Remove
  • 替换Replace
  • 分割Splitstring[]
  • 格式化Format("{0}{1}",…)
  • 大小写ToUpper(), ToLower()

StringBuilder(高效可变字符串)

  • 用途:频繁增删改字符时,减少内存分配

  • 容量Capacity 自动翻倍扩容;可在构造时指定初始容量

  • 常用方法:

    • Append / AppendFormat 添加
    • Insert 插入
    • Remove 删除
    • 索引器 sb[i] 读写
    • Replace 替换
    • Clear 清空
    • Equals 比较内容

结构体 vs 类

特性 结构体 (struct) 类 (class)
类型 值类型 引用类型
存储
继承 不能继承,只能实现接口 支持继承、多态
构造函数 不可定义无参;自定义有参不顶掉默认 可多重重载,有参顶掉默认需显式补无参
成员初始值 声明时不可赋初值 可赋初值
访问修饰符 protected 支持全部访问级别
静态成员 不支持 支持
自身字段引用 不能声明自身类型字段 可声明且不必初始化
  • 选用:数据小且多次复制不影响原值时用结构体;需继承、多态或大型对象用类

抽象类 vs 接口

特性 抽象类 (abstract class) 接口 (interface)
实例化
成员变量
构造函数
多继承 不支持(单继承) 支持(可继承多接口)
方法实现 可含抽象方法 & 虚方法 & 普通方法 仅声明,无实现
访问修饰符 支持全等级 默认 public,不建议写其他
何时用 表示对象抽象,共享字段+方法→子类实现 表示行为规范,可混入多功能

29.3 面试题

基础题

1. 面向对象三大特性

题目

请简述面向对象的三大特性,并分别说明它们的作用。

深入解析

面向对象的三大特性是封装、继承、多态,它们是面向对象编程的核心思想。

封装

  • 概念:将数据(字段)和行为(方法)包装在类中,通过访问修饰符控制访问权限
  • 作用:隐藏实现细节,保护数据安全,降低耦合
  • 体现:private字段 + public属性,外部只能通过属性访问

继承

  • 概念:子类继承父类的成员,实现代码复用
  • 作用:减少重复代码,建立类之间的层次关系
  • 特点:单根性(只能继承一个父类)、传递性(可继承祖先类成员)

多态

  • 概念:同一父类引用调用同名方法,执行子类不同实现
  • 作用:提高代码灵活性和扩展性,统一接口不同表现
  • 实现:virtual(虚方法)+ override(重写)
// 封装示例
class Person
{
    private int age;  // 私有字段
    public int Age    // 公有属性,控制访问
    {
        get { return age; }
        set { if (value >= 0) age = value; }
    }
}

// 继承示例
class Animal { public virtual void Speak() { } }
class Dog : Animal { public override void Speak() { Console.WriteLine("汪"); } }
class Cat : Animal { public override void Speak() { Console.WriteLine("喵"); } }

// 多态示例
Animal[] animals = { new Dog(), new Cat() };
foreach (var a in animals) a.Speak();  // 输出:汪 喵
答题示例

面向对象三大特性是封装、继承、多态。

封装是把数据和行为包装在类里,用访问修饰符控制访问,保护数据安全。

继承是子类复用父类代码,特点是单根性和传递性。

多态是同一父类引用调用方法时,执行子类的不同实现,用 virtual 和 override 实现。

参考文章
  • 2.面向对象-基本概念
  • 19.面向对象-多态-vob

2. 静态成员和静态类的特点

题目

请说明静态成员和静态类的特点,以及它们的使用场景。

深入解析

静态成员和静态类使用 static 关键字修饰,具有全局性和唯一性。

静态成员

  • 特点:程序启动即分配内存,直接用类名访问,生命周期与程序相同
  • 语法:ClassName.Member
  • 限制:静态方法中不能直接使用非静态成员

静态类

  • 特点:只能包含静态成员,不能实例化,不能被继承
  • 作用:工具类、扩展方法载体

静态构造函数

  • 特点:无访问修饰符、无参数、只执行一次
  • 作用:初始化静态成员
// 静态类示例
static class MathHelper
{
    public static float PI = 3.14159f;
    
    public static float CircleArea(float r)
    {
        return PI * r * r;
    }
}

// 使用
float area = MathHelper.CircleArea(5);  // 直接类名调用

// 静态构造函数
class Config
{
    public static string Version;
    
    static Config()  // 静态构造,只执行一次
    {
        Version = "1.0.0";
    }
}

使用场景

  • 工具方法:如数学计算、格式转换
  • 全局配置:如游戏设置、系统参数
  • 单例模式的基础
答题示例

静态成员用类名直接访问,程序启动就分配内存,生命周期和程序一样长。

静态类只能有静态成员,不能 new,常作为工具类。

静态构造函数没有修饰符和参数,只执行一次,用来初始化静态成员。

使用场景有工具方法、全局配置等。

参考文章
  • 9.面向对象-封装-静态成员
  • 10.面向对象-封装-静态类和静态构造函数

进阶题

1. 虚方法和抽象方法的区别

题目

请说明虚方法(virtual)和抽象方法(abstract)的区别,以及它们各自的使用场景。

深入解析

虚方法和抽象方法都用于实现多态,但有很多不同点。

核心区别

特性 虚方法 抽象方法
声明位置 普通类、抽象类 只能在抽象类中
方法体 必须有实现 不能有实现
子类重写 可选(用 override) 必须(用 override)
默认行为 有,子类可不重写 无,子类必须实现

虚方法

  • 父类提供默认实现,子类可选择是否重写
  • 适合:父类有合理的默认行为,子类可能需要定制

抽象方法

  • 父类只声明不实现,强制子类实现
  • 适合:父类无法提供合理默认值,必须由子类定义
abstract class Shape
{
    // 抽象方法:必须由子类实现
    public abstract decimal Area();
    
    // 虚方法:有默认实现,子类可选重写
    public virtual void Draw()
    {
        Console.WriteLine("绘制图形");
    }
}

class Circle : Shape
{
    public decimal Radius { get; set; }
    
    // 必须实现抽象方法
    public override decimal Area()
    {
        return (decimal)Math.PI * Radius * Radius;
    }
    
    // 可选重写虚方法
    public override void Draw()
    {
        Console.WriteLine("绘制圆形");
    }
}
答题示例

虚方法必须有方法体,子类可以选择是否重写;抽象方法没有方法体,子类必须重写。

抽象方法只能在抽象类里声明,虚方法可以在普通类和抽象类里。

如果父类能提供合理的默认行为,用虚方法;如果必须由子类定义具体实现,用抽象方法。

参考文章
  • 19.面向对象-多态-vob
  • 20.面向对象-多态-抽象类和抽象方法

2. 抽象类和接口的区别

题目

请说明抽象类和接口的区别,并举例说明如何选择使用抽象类还是接口。

深入解析

抽象类和接口都是不能实例化的类型,用于定义规范,但有本质区别。

核心区别

特性 抽象类 接口
成员变量 可以有 不能有
构造函数 可以有 不能有
方法实现 可以有抽象方法、虚方法、普通方法 只能声明,不能实现
继承 单继承 可继承多个接口
访问修饰符 支持各种级别 默认 public

选择原则

  • 抽象类:表示”是什么”,有共同字段和方法实现
  • 接口:表示”能做什么”,定义行为规范
// 抽象类:表示对象
abstract class Animal
{
    public string Name;  // 可以有字段
    public int Age;
    
    public abstract void Speak();  // 抽象方法
    
    public void Sleep()  // 普通方法
    {
        Console.WriteLine("睡觉");
    }
}

// 接口:表示行为
interface IFly
{
    void Fly();  // 只能声明
}

interface ISwim
{
    void Swim();
}

// 类可以继承一个抽象类 + 多个接口
class Duck : Animal, IFly, ISwim
{
    public override void Speak() { Console.WriteLine("嘎嘎"); }
    public void Fly() { Console.WriteLine("鸭子飞"); }
    public void Swim() { Console.WriteLine("鸭子游"); }
}

实际案例

  • 动物用抽象类(有共同属性:名字、年龄)
  • 飞翔用接口(鸟、蝙蝠、飞机都能飞,但它们不是同一类)
答题示例

抽象类可以有字段、构造函数和方法实现,接口只能声明成员。

类只能继承一个抽象类,但可以实现多个接口。

选择时,表示”是什么”用抽象类,表示”能做什么”用接口。

比如动物是抽象类,飞翔是接口,鸭子继承动物并实现飞翔接口。

参考文章
  • 20.面向对象-多态-抽象类和抽象方法
  • 21.面向对象-多态-接口
  • 28.面向对象关联知识点-抽象类和接口的区别

深度题

1. 装箱和拆箱的原理及性能影响

题目

请解释装箱和拆箱的原理,并说明它们对性能的影响以及如何避免不必要的装箱拆箱。

深入解析

装箱和拆箱是值类型与引用类型之间的转换机制。

装箱(Boxing)

  • 值类型 → 引用类型
  • 过程:在堆上创建对象,将值类型的值复制到堆中
  • 发生场景:object o = 123;

拆箱(Unboxing)

  • 引用类型 → 值类型
  • 过程:检查对象类型,将堆中的值复制到栈上
  • 发生场景:int i = (int)o;

性能影响

  1. 内存分配:装箱在堆上分配内存
  2. 内存复制:值从栈复制到堆(装箱)或从堆复制到栈(拆箱)
  3. GC压力:装箱产生的对象需要垃圾回收
// 装箱拆箱示例
int value = 100;
object obj = value;      // 装箱:值 → 堆上的对象
int result = (int)obj;   // 拆箱:堆上的对象 → 值

// 循环中的性能问题
ArrayList list = new ArrayList();
for (int i = 0; i < 10000; i++)
{
    list.Add(i);  // 每次都装箱!
}

// 优化方案:使用泛型
List<int> list2 = new List<int>();
for (int i = 0; i < 10000; i++)
{
    list2.Add(i);  // 无装箱
}

避免方法

  1. 使用泛型集合(List<int> 而非 ArrayList
  2. 使用 ToString() 而非装箱输出
  3. 重载方法避免 object 参数
// 避免装箱的写法
int num = 100;
Console.WriteLine(num.ToString());  // 好:调用 ToString
Console.WriteLine(num);             // 内部会调用 ToString,无装箱

// 方法重载避免 object 参数
void Print(int value) { }    // 值类型直接处理
void Print(string value) { } // 字符串直接处理
答题示例

装箱是把值类型转成引用类型,在堆上创建对象并复制值;拆箱是反过来,把堆上的值复制回栈。

性能影响包括:堆内存分配、值复制开销、增加 GC 压力。

避免方法:用泛型集合代替 ArrayList,用 ToString 代替直接传 object,方法重载避免 object 参数。

参考文章
  • 17.面向对象-继承-万物之父和装箱拆箱


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏