17.C#基础语法知识总结

17.总结


17.1 知识点

学习的主要内容

强调



17.2 核心要点速览

枚举

  • 定义:命名整型常量集合(默认从 0 起增,可手动赋值)
  • 声明enum E_Name { A, B = 5, C } (仅限 namespace/class/struct)
  • 使用E_Name x = E_Name.B; if(x==E_Name.A)… switch(x){…}
  • 转换(int)xx=(E_Name)ix.ToString()Enum.Parse(...)
  • 价值:替代魔法数,提升可读性。

数组

特性 一维数组 二维数组 交错数组
维度 1 2 N×可变(数组的数组)
声明 T[] a = {…}
new T[n]
T[,] a = new T[r,c] T[][] a = new T[n][]
长度 a.Length a.GetLength(0)a.GetLength(1) a.Length(行)
a[i].Length(列)
访问 a[i] a[i,j] a[i][j]
可变性 定长,扩缩需重建新数组 定行定列,扩缩需重建新数组 每行可单独扩缩
常用场景 线性数据、列表 矩阵运算、表格 行列不规则的数据集合
初始化示例 int[] a = {1,2,3} int[,] m = {{1,2},{3,4}} int[][] j = { new[]{1,2}, new[]{3} }

值和引用

使用和存储

特性 值类型 引用类型
存储位置 栈(Stack) 堆(Heap)
赋值行为 复制实际值 → 独立副本 复制引用 → 共享同一对象
修改影响 改变副本,不影响原变量 改变对象,所有引用都会看到
示例 int a=10; int b=a; int[] a={1}; int[] b=a;
典型类型 整数、浮点、struct 数组、stringclass
特殊说明 string 不可变,赋新值时会指向新对象

核心

  • 值类型 → 快、小、复制安全;
  • 引用类型 → 灵活、共享同一数据;
  • 选用:按数据大小与共享需求决定。

string

特性 string
类型 引用类型(特殊)
赋值行为 复制引用,但重写赋值会创建新实例
可变性 不可变(Immutability)
存储位置 堆(Heap)
性能影响 频繁拼接会产生垃圾,建议使用 StringBuilder 优化
典型用法 文本处理、日志、格式化输出

核心

  • 虽为引用类型,却表现为”它变我不变”(不可变)。
  • 每次赋新值都在堆上创建新对象。
  • 大量字符串操作要用 StringBuilder 或等效结构以减少内存开销。

函数

函数基础

  • 定义:命名的代码块,用于封装、复用、抽象。

  • 位置:只能写在 classstruct 中,不能嵌套在方法里。

  • 语法

    static 返回类型 名称(参数列表) { … [return 值]; }
    

    void 必须 return

refout

  • 作用:让函数内修改影响调用者。

  • 区别

    • ref:参数必须先初始化,内部可赋可不赋。
    • out:参数无需初始化,内部必须赋值。
  • 调用

    FooRef(ref x);
    FooOut(out y);
    

变长参数 params

  • 用途:允许 0…n 个同类型实参。

  • 要点:只能有一个,写在参数列表末尾,类型为数组。

    static int Sum(params int[] nums) { … }
    

可选参数(默认值)

  • 用途:未传值时使用默认值。

  • 要点:所有默认参数必须放在必选参数之后。

    static void Speak(string msg = "…") { … }
    

函数重载

  • 定义:同名方法通过参数数量、类型、顺序区分;与返回值无关

  • 示例

    Calc(int a,int b);
    Calc(float a,float b);
    Calc(int a,params int[] xs);
    

递归

  • 定义:函数自己调用自己。

  • 必须

    1. 存在可变的终止条件。
    2. 终止条件能被满足。
  • 示例

    static void Fun(int n) {
      if (n>10) return;
      Console.WriteLine(n);
      Fun(n+1);
    }
    

结构体

  • 概念:自定义的值类型,字段 + 方法 的集合,用于表示一组相关数据(如学生、矩形、玩家等)。

  • 声明位置:只能写在 namespaceclass/struct 外层,不能在方法内。

  • 语法

    struct Name {
      // 字段(变量)
      public 类型 字段1;
      …
      // 构造函数(可选)
      public Name(参数…) { this.字段=…; … }
      // 方法(无需 static)
      public void Func() { … }
    }
    
  • 访问修饰符

    • public 允许外部访问
    • private(默认)仅内部可见
  • 使用

    // 1. 声明
    MyStruct s;
    // 2. 赋值字段
    s.Field = …;
    // 3. 调用方法
    s.Method();
    // 或使用构造器
    var s2 = new MyStruct(arg1, arg2, …);
    
  • 构造函数要点

    • 无返回值,名与 struct 同名
    • 必须为所有字段赋初值
  • 重要限制

    • 字段不能直接初始化:声明时不能赋值,只能在构造函数或外部赋值
    • 方法无需 static:结构体内方法不加 static,直接通过实例调用
  • 特点

    • 值类型 ⇒ 赋值/传参时复制整个实例
    • 轻量级,适合存储小数据集合,无需 GC 开销。

排序算法

冒泡排序

  • 原理:两两相邻比较,不停交换,每轮将极值”冒泡”到正确位置。

  • 套路:两层循环,外层轮数,内层比较,满足条件交换。

  • 代码

    bool isSort = false;
    for (int m = 0; m < arr.Length; m++)
    {
        isSort = false;
        for (int n = 0; n < arr.Length - 1 - m; n++)
        {
            if (arr[n] > arr[n + 1])  // 升序;降序改为 <
            {
                isSort = true;
                int temp = arr[n];
                arr[n] = arr[n + 1];
                arr[n + 1] = temp;
            }
        }
        if (!isSort) break;  // 优化:已有序则提前退出
    }
    
  • 优化

    1. 每轮确定的极值不再参与比较(arr.Length - 1 - m
    2. bool 标识判断是否已有序

选择排序

  • 原理:每轮找出极值,放入目标位置。排序区逐渐增大,未排序区逐渐减小。

  • 套路:两层循环,外层轮数,内层寻找极值索引,内层循环外交换。

  • 代码

    for (int m = 0; m < arr.Length; m++)
    {
        int index = 0;  // 记录极值索引
        for (int n = 1; n < arr.Length - m; n++)
        {
            if (arr[index] < arr[n])  // 升序找最大;降序改为 >
                index = n;
        }
        if (index != arr.Length - 1 - m)  // 避免无意义交换
        {
            int temp = arr[index];
            arr[index] = arr[arr.Length - 1 - m];
            arr[arr.Length - 1 - m] = temp;
        }
    }
    

对比

特性 冒泡排序 选择排序
交换次数 多(频繁交换) 少(每轮最多一次)
稳定性 稳定 不稳定
实现难度 简单 简单
时间复杂度 O(n²) O(n²)
适用场景 数据量小、基本有序时较优 数据量小、交换成本高时优

17.3 面试题精选

基础题

1. 值类型和引用类型的区别

题目

请简述 C# 中值类型和引用类型的区别,并举例说明它们在赋值时的行为差异。

深入解析

值类型和引用类型的核心区别在于存储位置赋值行为

存储位置

  • 值类型:存储在栈(Stack),系统自动分配和回收,访问速度快
  • 引用类型:存储在堆(Heap),需要手动申请,GC 负责回收

赋值行为

  • 值类型:复制的是实际值,修改副本不影响原变量(它变我不变)
  • 引用类型:复制的是引用(地址),两个变量指向同一对象(它变我也变)
int a = 10;
int b = a;      // 值类型:b 得到 a 的副本
b = 20;
Console.WriteLine(a);  // 输出 10,a 不受影响

int[] arr1 = { 1, 2, 3 };
int[] arr2 = arr1;     // 引用类型:arr2 和 arr1 指向同一数组
arr2[0] = 99;
Console.WriteLine(arr1[0]);  // 输出 99,arr1 也变了

典型类型

  • 值类型:int、float、bool、char、struct、enum
  • 引用类型:数组、string、class
答题示例

值类型存在栈上,引用类型存在堆上。

赋值的时候,值类型是复制实际值,改了副本不影响原变量;引用类型是复制地址,两个变量指向同一个对象,改一个另一个也跟着变。

比如 int 是值类型,数组是引用类型。

参考文章
  • 7.值类型和引用类型-使用和存储上的区别
  • 8.值类型和引用类型-特殊的引用类型string

2. ref 和 out 的区别

题目

ref 和 out 关键字有什么作用?它们之间有什么区别?

深入解析

ref 和 out 都用于让函数内部的修改影响外部变量,本质上都是传递引用而非值拷贝。

核心区别

关键字 调用前是否必须初始化 函数内是否必须赋值
ref 必须 可选
out 不需要 必须

使用场景

  • ref:需要在函数内修改外部已初始化的变量
  • out:函数需要返回多个值,调用者不需要预先初始化
static void TestRef(ref int x)
{
    x = 100;  // 可以改,也可以不改
}

static void TestOut(out int x)
{
    x = 100;  // 必须赋值,否则编译报错
}

// 调用
int a = 0;           // ref 必须先初始化
TestRef(ref a);

int b;               // out 不需要初始化
TestOut(out b);
答题示例

ref 和 out 都是让函数里改的值能传到外面去。

区别有两点:

  • 第一,ref 传进去之前必须先初始化,out 不用
  • 第二,out 在函数里必须赋值,ref 可改可不改

一般用 out 来返回多个值,用 ref 来修改已有的变量。

参考文章
  • 10.函数-ref和out

进阶题

1. string 为什么是特殊的引用类型

题目

string 是引用类型,但为什么表现出”它变我不变”的特性?这在性能上有什么影响?

深入解析

string 虽然是引用类型,但它是不可变(Immutable)的。每次对 string 变量重新赋值或修改,都会在堆上创建一个全新的字符串对象。

原理

string str1 = "123";
string str2 = str1;    // str2 指向 "123"
str2 = "321";          // str2 指向新对象 "321",str1 仍然是 "123"

C# 底层对 string 做了特殊处理,每次赋新值相当于 new 了一个新字符串,而不是修改原对象。

性能影响

  • 频繁拼接字符串会产生大量临时对象,增加 GC 压力
  • 解决方案:使用 StringBuilder 进行大量字符串操作
// 不推荐:每次循环都创建新字符串对象
string s = "";
for (int i = 0; i < 1000; i++)
{
    s += i;  // 产生大量垃圾
}

// 推荐:StringBuilder
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int i = 0; i < 1000; i++)
{
    sb.Append(i);
}
string result = sb.ToString();
答题示例

string 是引用类型,但它是不可变的。

每次给它赋新值,底层其实是 new 了一个新字符串对象,原来的不变。所以看起来像值类型的”它变我不变”。

性能上的影响是,频繁拼接字符串会产生很多临时对象,增加 GC 压力,大量操作时建议用 StringBuilder。

参考文章
  • 7.值类型和引用类型-使用和存储上的区别
  • 8.值类型和引用类型-特殊的引用类型string

2. 结构体的特点和限制

题目

C# 中结构体(struct)有什么特点?声明时有哪些限制?

深入解析

结构体是自定义的值类型,是字段和方法的集合,适合表示一组相关的小数据。

核心特点

  • 值类型:赋值和传参时复制整个实例
  • 轻量级:存储在栈上,无 GC 开销
  • 适合场景:小数据集合,如坐标点、颜色、矩形等

声明限制

  1. 字段不能直接初始化(声明时不能赋值)
  2. 构造函数必须为所有字段赋初值
  3. 方法不需要 static 关键字
struct Player
{
    public string name;    // 不能写 = "默认名"
    public int hp;
    
    // 构造函数必须初始化所有字段
    public Player(string name, int hp)
    {
        this.name = name;
        this.hp = hp;
    }
    
    // 方法不加 static
    public void TakeDamage(int damage)
    {
        hp -= damage;
    }
}

// 使用
Player p1 = new Player("勇者", 100);
Player p2 = p1;          // 值类型:p2 是 p1 的副本
p2.hp = 50;
Console.WriteLine(p1.hp);  // 输出 100,p1 不受影响
答题示例

结构体是值类型,赋值的时候是复制整个实例,不是复制引用。

声明的时候有几个限制:

  • 字段不能直接赋初值
  • 构造函数必须初始化所有字段
  • 方法不需要加 static

适合存小数据,比如坐标、颜色这种,没有 GC 开销。

参考文章
  • 14.复杂数据类型-结构体

深度题

1. 冒泡排序和选择排序的区别

题目

冒泡排序和选择排序的原理分别是什么?它们在交换次数和稳定性上有什么区别?

深入解析

两种排序时间复杂度都是 O(n²),但实现方式和特性不同。

冒泡排序

  • 原理:两两相邻比较,不停交换,每轮把极值”冒泡”到正确位置
  • 交换次数:多,频繁交换
  • 稳定性:稳定(相等元素不交换,相对位置不变)
bool isSort = false;
for (int m = 0; m < arr.Length; m++)
{
    isSort = false;
    for (int n = 0; n < arr.Length - 1 - m; n++)
    {
        if (arr[n] > arr[n + 1])  // 升序
        {
            isSort = true;
            int temp = arr[n];
            arr[n] = arr[n + 1];
            arr[n + 1] = temp;
        }
    }
    if (!isSort) break;  // 优化:已有序则提前退出
}

选择排序

  • 原理:每轮找出极值索引,放到目标位置
  • 交换次数:少,每轮最多一次
  • 稳定性:不稳定(可能改变相等元素的相对位置)
for (int m = 0; m < arr.Length; m++)
{
    int index = 0;
    for (int n = 1; n < arr.Length - m; n++)
    {
        if (arr[index] < arr[n])  // 找最大值索引
            index = n;
    }
    if (index != arr.Length - 1 - m)  // 避免无意义交换
    {
        int temp = arr[index];
        arr[index] = arr[arr.Length - 1 - m];
        arr[arr.Length - 1 - m] = temp;
    }
}

对比总结

特性 冒泡排序 选择排序
交换次数 多(频繁交换) 少(每轮最多一次)
稳定性 稳定 不稳定
适用场景 数据量小、基本有序时优 数据量小、交换成本高时优
答题示例

冒泡是两两比较,满足条件就交换,每轮把最大的冒到最后。

选择是每轮找最大值的索引,最后只交换一次。

冒泡交换次数多但是稳定,选择交换少但不稳定。

如果数据基本有序,冒泡有优化空间;如果交换成本高,选择更合适。

参考文章
  • 15.排序-冒泡排序
  • 16.排序-选择排序


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

×

喜欢就点赞,疼爱就打赏