31.C#进阶语法知识总结

  1. 31.总结
    1. 31.1 知识点
      1. 学习的主要内容
      2. 注意
      3. 强调
    2. 31.2 核心要点速览
      1. 数据结存储概念回顾
      2. 简单数据结构类
      3. 泛型
        1. 泛型相关分类对比
        2. 泛型约束(where)
        3. 何时用泛型
      4. 常用泛型数据结构
      5. 委托(Delegate)
      6. 事件(Event)
      7. 匿名函数(Anonymous Function)
      8. Lambda 表达式
      9. 闭包(Closure)
      10. List.Sort
      11. 协变(Covariance) 和 逆变(Contravariance)
        1. 语法示例
        2. 接口中使用
        3. 适用范围
      12. 多线程
        1. 理解进程 vs 线程
        2. 启动新线程的关键步骤
        3. 线程控制与调度
        4. 线程间共享数据与加锁
        5. 多线程的价值
      13. 编译器与预处理器指令
      14. 反射
        1. 程序集和元数据
        2. 什么是反射
        3. 反射相关核心类与用途
        4. 使用反射的经典场景
        5. 注意事项
      15. 特性
        1. 特性概念
        2. 自定义特性
        3. 特性使用
      16. 反射读取特性
        1. 限制使用范围
        2. 常用系统特性
      17. 迭代器
        1. 迭代器作用
        2. 实现标准迭代器(传统写法)
        3. foreach 本质流程
        4. yield return 语法糖实现
        5. 泛型迭代器实现(配合 yield return)
        6. 迭代器总结
      18. 特殊写法
        1. var隐式类型
        2. 对象初始化简写
        3. 集合初始化简写
        4. 匿名类型
        5. 可空类型 Nullable
        6. 空合并操作符 ??
        7. 字符串内插
        8. 单句逻辑简写
      19. 值和引用知识补充
        1. 如何判断值类型与引用类型
        2. 语句块与变量作用域
        3. 变量生命周期
        4. 使用高层变量记录底层变量
        5. 结构体中的值类型与引用类型
        6. 类中的值类型与引用类型
        7. 数组存储规则
        8. 结构体继承接口(装箱/拆箱)

31.总结


31.1 知识点

学习的主要内容

注意

强调



31.2 核心要点速览

数据结存储概念回顾

  • 顺序存储(Array, List)

    • 元素紧密连续
    • :O(1)
    • 增/删(中间):O(n)
  • 链式存储(LinkedList)

    • 通过指针链接
    • 增/删:O(1)(在已知节点位置)
    • 查/改:O(n)(需遍历)

简单数据结构类

容器 底层结构 存取规则 常用操作 遍历方式
ArrayList 可变长 object[] 任意索引增删改 - 增:AddAddRangeInsert
- 删:RemoveRemoveAtClear
- 查改:索引器 list[i] get/set、ContainsIndexOfLastIndexOf
- for (i=0…Count-1)
- foreach
- 查看容量:Count / Capacity
Stack 后进先出(LIFO) 只能顶端进出 - Push(item)
- Pop()(移除并返回顶)
- Peek()(查看顶,不移除)
- ContainsClearCount
- foreach(从顶到底)
- ToArray() + for
- while (Pop())
Queue 先进先出(FIFO) 只能队首进队首出 - Enqueue(item)
- Dequeue()(移除并返回队首)
- Peek()(查看队首,不移除)
- ContainsClearCount
- foreach(从队首到队尾)
- ToArray() + for
- while (Dequeue())
Hashtable 键/值对散列表 按键存取 - Add(key,value)
- Remove(key)Clear
- 索引器 ht[key] get/set
- ContainsKeyContainsValue
- foreach (DictionaryEntry kv in hashtable)
- foreach (var k in Keys) + ht[k]
- IDictionaryEnumerator

Tip: 如类型已知,优先使用泛型版本(List<T>/Stack<T>/Queue<T>/Dictionary<TKey,TValue>),以避免装箱拆箱并增强类型安全。

泛型

泛型相关分类对比

分类 语法示例 说明
泛型类 class MyList<T> 在声明时使用类型占位符T,使用时指定具体类型。
泛型接口 interface IRepository<T> 与泛型类类似,可定义泛型属性/方法。
泛型方法 void Swap<T>(ref T a, ref T b) 方法级别的类型占位,可与泛型类并存;占位符可多个。

泛型约束(where

约束符 形式 含义 例子
struct where T : struct T 必须是值类型(包括所有结构体) Test1<T> where T: struct
class where T : class T 必须是引用类型 Test2<T> where T: class
new() where T : new() T 必须有公共无参构造函数 Test3<T> where T: new()
类名 where T : BaseType T 必须是指定类或其派生类 Test4<T> where T: Animal
接口名 where T : IFly T 必须实现指定接口 Test5<T> where T: IFly
泛型参数 where T : U T 必须是另一个泛型参数 U 或其派生类 Test6<T,U> where T: U

组合示例

class Manager<T, K>
    where T : class, new()
    where K : struct
{ }

何时用泛型

  • 代码复用:同一数据结构/算法,可用于多种类型。
  • 类型安全:避免向 object 容器中装箱拆箱。
  • 性能优化:减少运行时类型转换和垃圾产生。

常用泛型数据结构

容器 本质 常用操作 遍历方式
List<T> 可变长度的泛型数组 AddAddRangeInsert
RemoveRemoveAtClear
list[i] = x
索引器 list[i]
Contains
IndexOf/LastIndexOf
for(int i=0;i<Count;i++)
foreach(var x in list)
Dictionary<K,V> 泛型的键/值对哈希表 Add(key,value)
Remove(key)Clear
dict[key] = v
索引器 dict[key]
TryGetValue
ContainsKey/ContainsValue
foreach(var kv in dict)
foreach(var k in Keys)
LinkedList<T> 泛型的双向链表 AddFirstAddLastAddBeforeAddAfter
RemoveRemoveFirstRemoveLast
node.Value = x
First/Last
Find(item)
Contains
foreach(var x in ll)
for(node=First; node!=null; node=node.Next)
Stack<T> 泛型的后进先出栈(LIFO) Push(item)
Pop()
Peek()
Clear()
Contains(item) foreach(var x in stack)
while(stack.Count>0){ var x=stack.Pop(); }
Queue<T> 泛型的先进先出队列(FIFO) Enqueue(item)
Dequeue()
Peek()
Clear()
Contains(item) foreach(var x in queue)
while(queue.Count>0){ var x=queue.Dequeue(); }

委托(Delegate)

  1. 概念

    • 委托是“方法的容器”——将方法视为可存储、传递的第一类对象。
    • 定义了一种签名(返回值类型 + 参数列表),只有与该签名完全匹配的方法才能被存入。
  2. 声明语法

    // namespace 或 class 中都可声明
    [public] delegate 返回类型 委托名(参数类型1 名称1, 参数类型2 名称2, …);
    
    • public delegate void MyAction(int x, string y);
    • 与方法声明几乎相同,只是前置 delegate 关键字。
  3. 绑定与调用

    MyAction a1 = new MyAction(SomeMethod);
    MyAction a2 = SomeMethod;        // 语法糖
    a1(arg1, arg2);                  // 等价于 a1.Invoke(arg1, arg2);
    
    • 绑定时不带括号;调用时既可使用 Invoke 也可直接像方法一样写 a1()
  4. 多播功能

    • 可以向一个委托变量中累加多个方法:

      a += MethodA;
      a += MethodB;
      
    • 调用时会按添加顺序依次执行;用 -= 移除首个匹配的方法。

    • 执行前要判空: if (a != null) a();

  5. 作为成员和参数

    • 成员字段:用于类内部或外部的事件/回调存储。

      public MyAction OnComplete;
      
    • 方法参数:允许调用者传入自己的逻辑到方法内部延后执行。

      void DoSomething(MyAction callback) { … callback(); }
      
  6. 系统内置委托

    • Action:无返回值,支持 0–16 个输入参数。
    • **Func<…>**:有返回值,支持 0–16 个输入参数,最后一个泛型参数为返回类型。
  7. 使用场景

    • 事件处理与回调机制(UI 事件、异步完成通知等)
    • 策略模式或动态行为切换
    • LINQ、异步编程及并行执行的基础

总结:委托让方法可以像数据一样被存储与传递,是 C# 中实现事件、回调和灵活扩展的关键。

Tip: 委托本质上是一个类,delegate 关键字只是声明了其 Invoke 签名;多播、组合、延迟调用让它成为 C# 事件和 LINQ 中不可或缺的基石。

事件(Event)

  1. 概念

    • 事件是“安全封装”的委托,只能在声明它的类/结构体/接口内部被触发或清空。
    • 用于发布—订阅模式,防止外部随意重置或调用委托。
  2. 声明语法

    [public] event 委托类型 事件名;
    // 例如:
    public event Action OnCompleted;
    
  3. 与委托的异同

    特性 委托(Delegate) 事件(Event)
    赋值/清空 在任何地方可用 = 只能在类内部可用 +=/-=
    触发/调用 在任何地方可直接 Invoke() 只能在声明它的类内部触发(调用)
    作为局部变量 可以 不可(编译器禁止)
    主要用途 通用回调、函数指针模拟 发布—订阅、安全的回调管理
  4. 使用流程

    1. 在类中声明事件:

      public event Action DataReady;
      
    2. 外部订阅/退订:

      obj.DataReady += Handler;    // 注册处理器
      obj.DataReady -= Handler;    // 注销处理器
      
    3. 内部触发:

      if (DataReady != null)
          DataReady();             // 或 DataReady.Invoke();
      

总结:事件只是对委托的一层保护,让你只能通过 +=/-= 注册或注销回调,不会被外部任意赋空或直接调用,从而实现更安全、更规范的发布—订阅机制。

匿名函数(Anonymous Function)

  1. 定义

    • 没有名称的方法体,可直接内联声明。

    • 形如:

      delegate(参数列表)
      {
          // 方法体
      };
      
  2. 何时使用

    • 作为委托参数:简化回调传递,无需额外命名方法。
    • 作为委托返回值:动态构建并返回方法逻辑。
    • 典型场景:事件处理器、LINQ 查询、异步回调等。
  3. 基本语法变体

    // 无参无返回
    Action a = delegate() { /*…*/ };
    
    // 有参无返回
    Action<int, string> a2 = delegate(int x, string s) { /*…*/ };
    
    // 无参有返回
    Func<string> f = delegate() { return "hello"; };
    
    // 直接传递给方法
    obj.DoSomething(delegate(int x) { Console.WriteLine(x); });
    
  4. 优点

    • 内联:无需额外命名、减少样板代码。
    • 临时性:回调逻辑就地定义,更具可读性。
  5. 缺点

    • 无法单独移除:因为没有名称,delegate { … } 无法用 -= 精确移除单个匿名条目。
    • 调试:栈跟踪里看不到方法名,仅显示“anonymous method”。
  6. 最佳实践

    • 小而精的回调逻辑适合匿名函数。
    • 复杂或需多次移除的处理器,应使用具名方法并存到变量中,以便管理。

Lambda 表达式

  • 定义:匿名函数的简洁写法,用 => 分隔参数和函数体。

  • 语法

    (参数列表) => { /* 函数体 */ };
    

    与匿名函数等价:

    delegate(参数列表) { /* 函数体 */ };
    
  • 示例

    // 无参无返回
    Action a = () => Console.WriteLine("Hello");
    
    // 单参无返回(类型可省略)
    Action<int> a2 = x => Console.WriteLine(x);
    
    // 多参有返回
    Func<int, int, int> add = (x, y) => x + y;
    
  • 使用场景

    • 传递给需要委托/事件的 API(如 LINQ、事件绑定)。
    • 简化一次性短逻辑,无需额外命名。
  • 优缺点

    • 优点:更简洁、易读,省去 delegate 关键字和方法名。
    • 缺点:与匿名函数一样,无法精确地从多播委托/事件中移除单个 lambda。

闭包(Closure)

  • 概念:函数(或委托)连同其定义时所处环境(局部变量)一起“打包”,并保持对这些外部变量的访问能力,即使外层作用域已结束。

  • 核心特点

    • 捕获的是变量本身的引用,而非当时的值。
    • 外层局部变量的生命周期被延长,直到所有引用它的闭包都不再使用为止。
  • 示例

    Func<int, int> Counter(int increment)
    {
        int count = 0;                   // 被捕获的外部变量
        return number => {               // 闭包 lambda
            count += increment;
            return number + count;
        };
    }
    
    var c = Counter(5);
    Console.WriteLine(c(10)); // 15  (count从0变为5)
    Console.WriteLine(c(20)); // 25  (count从5变为10)
    
  • for 循环与闭包陷阱

    for (int i = 0; i < 3; i++)
    {
        list.Add(() => Console.WriteLine(i));
    }
    // 调用后,所有 lambda 都打印“3”……
    

    解法:在循环体内引入新的局部变量:

    for (int i = 0; i < 3; i++)
    {
        int copy = i;
        list.Add(() => Console.WriteLine(copy)); // 捕获 copy 而非 i
    }
    
  • 注意事项

    • 被捕获的局部变量只释放于闭包不再引用它时。
    • 过度使用闭包可能隐藏内存泄漏风险。

List.Sort

  1. 默认排序

    • 方法list.Sort()
    • 要求元素类型:必须实现 IComparable<T>(或非泛型 IComparable)。
    • 行为:调用元素的 CompareTo,小于 0 放前面,等于 0 保持, 大于 0 放后面;默认升序。
  2. 自定义类型实现接口排序

    • 接口IComparable<Item>

    • 实现

      public class Item : IComparable<Item>
      {
          public int money;
          public int CompareTo(Item other)
              => this.money.CompareTo(other.money);
      }
      
    • 调用

      var items = new List<Item>{...};
      items.Sort();  // 调用 Item.CompareTo
      
  3. 传委托 Comparison 排序

    • 方法重载list.Sort(Comparison<T> comparison)

    • 定义 Comparison 函数

      static int CompareShop(ShopItem a, ShopItem b)
          => a.id.CompareTo(b.id);
      
    • 调用

      shopList.Sort(CompareShop);
      
    • 匿名函数

      shopList.Sort(delegate(ShopItem a, ShopItem b) {
          return a.id - b.id;
      });
      
    • Lambda 写法

      shopList.Sort((a,b) => a.id - b.id);
      
  4. 自定义排序要点

    • 返回值<0 → 前, 0 → 不变, >0 → 后。
    • 升序 vs 降序:调换返回值符号(或参数顺序)可实现降序。
    • 性能List<T>.Sort 使用快速排序和插入排序的混合实现,平均 O(n log n)。

协变(Covariance) 和 逆变(Contravariance)

特性 关键字 修饰位置 用途限制 里氏替换示例
协变 out 泛型接口 / 泛型委托 只能用于返回值(不能作参数) TestOut1<Father> f = sonProvider;
Func<Son> 可赋给 Func<Father>
逆变 in 泛型接口 / 泛型委托 只能用于参数(不能作返回值) TestIn1<Son> s = fatherConsumer;
Action<Father> 可赋给 Action<Son>

语法示例

  • 协变委托

    public delegate T Producer<out T>();
    Producer<Son> sonProducer = () => new Son();
    Producer<Father> fatherProducer = sonProducer; // OK (Son → Father)
    
  • 逆变委托

    public delegate void Consumer<in T>(T arg);
    Consumer<Father> fatherConsumer = f => { /*…*/ };
    Consumer<Son> sonConsumer = fatherConsumer; // OK (Father → Son)
    

接口中使用

  • 协变接口

    interface IReadOnly<out T>
    {
        T GetItem(int index);
        // void Add(T item); // ❌ 不允许
    }
    
  • 逆变接口

    interface IWriteOnly<in T>
    {
        void Add(T item);
        // T GetItem(); // ❌ 不允许
    }
    

适用范围

  • 仅限:泛型 接口泛型委托
  • 不适用:泛型类、结构体等(会编译报错)

核心思想:用 out 标记“生产者”→只输出;用 in 标记“消费者”→只输入,二者都借助里氏替换原则让“父⇄子”泛型能够安全转换。

多线程

理解进程 vs 线程

  • 进程(Process):程序在操作系统中的一次运行实例,拥有独立的内存和资源。
  • 线程(Thread):进程内可并发执行的最小调度单位,共享所在进程的资源和内存。

启动新线程的关键步骤

  1. 声明循环控制标识(若线程内部需要持续运行/可停止):

    static bool isRunning = true;
    
  2. 封装线程逻辑方法(无参无返回值):

    static void NewThreadLogic()
    {
        while (isRunning)
        {
            // … 执行周期性或耗时任务 …
        }
    }
    
  3. 创建并启动线程(需 using System.Threading;):

    Thread t = new Thread(NewThreadLogic);
    t.IsBackground = true;  // 设置为后台线程
    t.Start();              // 启动
    
  4. 结束线程

    • 安全停止:在主线程中 isRunning = false;,让循环自然退出

    • 强制终止(.NET Core 不推荐/可能不支持):

      t.Abort();
      t = null;
      

线程控制与调度

  • Thread.Sleep(ms):让当前线程休眠指定毫秒。
  • IsBackground:将线程标记为后台,主线程结束时后台线程随进程一同退出。

线程间共享数据与加锁

  • 进程内所有线程共享同一内存,并发访问同一资源可能造成冲突。

  • 锁(lock) 用法:

    static object lockObj = new object();
    …
    lock (lockObj)
    {
        // 仅允许一个线程进入此代码块
    }
    
  • 场景示例:一个线程绘制红色圆,一个线程绘制黄色方块,通过 lock 保证绘制命令的原子性,避免光标定位和颜色设置交叉干扰。

多线程的价值

  • 耗时操作(如网络请求、寻路、文件 I/O、复杂计算)放到子线程,避免阻塞主线程,保持界面或逻辑的流畅响应。

多线程就是在同一进程中开启多条“执行管道”,善用后台线程、循环控制、线程睡眠和锁机制,可让耗时任务与主逻辑并行、安全协作。

编译器与预处理器指令

  • 编译器:将高级语言翻译为机器能识别的中间代码或目标语言(如 DLL 或 EXE)。

  • 预处理器指令:以 # 开头,用于编译前的判断与控制,不以分号结尾。

  • 常用指令

    • #define / #undef:定义或取消符号。
    • #if / #elif / #else / #endif:条件编译,控制不同代码块是否参与编译。
    • #warning / #error:编译时发出警告或终止报错。
    • #region / #endregion:代码折叠用,提升可读性。
  • 用途

    • 实现跨平台或版本适配(如判断 Unity 版本)。
    • 控制测试/调试代码是否参与编译。
    • 提前在编译前排除无效路径,提高开发效率。

反射

程序集和元数据

  • 程序集:编译后的代码集合(DLL/EXE),包含元数据、IL代码等。
  • 元数据:程序中的结构信息(类、字段、方法等),用于描述程序自身。

什么是反射

  • 反射:程序运行时动态查看、调用、修改自身或其他程序集的结构和行为(类型、构造、字段、方法等)。
  • 意义:提升程序扩展性、灵活性,可用于插件、脚本系统、序列化、Unity编辑器拓展等。

反射相关核心类与用途

  • Type

    • 获取类型信息:typeof(ClassName)obj.GetType()Type.GetType("全名")
    • 获取成员:GetFields()GetMethods()GetConstructors()GetMembers()
    • 获取特定成员并操作:GetField("name")GetMethod("name")、使用 SetValue/GetValue/Invoke
  • ConstructorInfo / FieldInfo / MethodInfo

    • 反射构造函数并 Invoke() 实例化对象
    • 获取/设置字段值
    • 调用方法
  • Activator

    • 快速实例化对象:Activator.CreateInstance(type, 参数...)
    • 不需要手动查找构造函数,更简洁
  • Assembly

    • 动态加载程序集(DLL):Load() / LoadFrom() / LoadFile()
    • 通过程序集获取类型并反射使用其中的类

使用反射的经典场景

  • 动态插件加载
  • 编辑器工具开发(如自定义 Inspector)
  • 自定义序列化/反序列化
  • 事件总线、属性注入(依赖注入)

注意事项

  • 反射性能略低,不宜频繁使用
  • 非公共成员默认无法访问,需指定 BindingFlags 或使用私有访问策略
  • 参数类型不匹配、类不存在等问题会导致运行时报错(非编译期)

特性

特性概念

  • 本质是类,为程序结构添加元数据
  • 可修饰类、方法、字段等,配合反射读取使用。

自定义特性

  • 继承自 Attribute
  • 命名惯例:XXXAttribute(使用时可省略后缀)。
  • 构造函数决定传参格式。

特性使用

  • 语法:[特性名(参数)]
  • 放在声明上一行,实质是构造函数调用。

反射读取特性

  • IsDefined():判断是否应用某特性。
  • GetCustomAttributes():获取全部特性对象并强转使用。

限制使用范围

  • [AttributeUsage] 指定可用位置、是否允许重复、是否可继承。

常用系统特性

  • Obsolete:标记过时成员,支持警告或错误提示。
  • 调用者信息CallerFilePath / CallerLineNumber / CallerMemberName
  • Conditional:配合 #define 控制方法是否编译。
  • DllImport:绑定调用 C/C++ DLL 中的方法。

迭代器

迭代器作用

  • 提供统一方式遍历集合,不暴露内部结构。
  • 可用 foreach 遍历的类型,都实现了迭代器模式。

实现标准迭代器(传统写法)

  • 实现接口:IEnumerable(用于遍历) + IEnumerator(用于控制位置)。

  • 核心方法:

    • GetEnumerator():返回自身(IEnumerator);
    • MoveNext():移动光标;
    • Current:返回当前项;
    • Reset():重置光标(避免 foreach 多次使用时无效)。

foreach 本质流程

  1. 调用对象的 GetEnumerator()
  2. 不断调用 MoveNext() → 若为 true 则读取 Current
  3. 否则结束循环。

yield return 语法糖实现

  • 只需继承 IEnumerable,简化逻辑。
  • 系统自动生成 IEnumerator 实现。
  • 每次 yield return 会记录当前位置并在下次继续。

泛型迭代器实现(配合 yield return)

  • 自定义类继承 IEnumerable
  • yield return 返回泛型元素,无需额外泛型接口。
  • 可用于任意类型集合的遍历。

迭代器总结

  • 迭代器支持 foreach 的根本。

  • 两种方式实现:

    • 传统:实现 IEnumerator + IEnumerable
    • 简洁:使用 yield return + IEnumerable
  • 重点掌握 yield return,写法简单且易维护。

特殊写法

var隐式类型

  • var 只能用于局部变量,且必须初始化。
  • 推导类型后不可更改;适合临时变量或简化代码。

对象初始化简写

  • 使用 {} 初始化对象的公共字段和属性。
  • 可与构造函数同时使用,花括号内赋值优先级更高

集合初始化简写

  • 数组、List、Dictionary 可在声明时使用 {} 初始化元素。
  • List 可以包含自定义对象,支持嵌套初始化。

匿名类型

  • 使用 var obj = new {} 创建临时、无命名的类型。
  • 只支持字段,不支持方法。
  • 常用于局部临时数据封装。

可空类型 Nullable

  • 值类型后加 ? 可为空(如 int?),底层是 Nullable<T>

  • 常用属性方法:

    • HasValue:是否有值;
    • Value:取值;
    • GetValueOrDefault():空时返回默认值;
    • ?.:语法糖,判空后再操作。

空合并操作符 ??

  • 表达式:a ?? b
  • a 为 null,返回 b;否则返回 a
  • 可用于引用类型和可空值类型的默认值处理。

字符串内插

  • 使用 $"{变量}" 进行字符串拼接,更清晰简洁

单句逻辑简写

  • if / for / while 等语句中只有一句代码时可省略 {}

  • 支持属性、方法的简写:

    • get => value;
    • 方法名 => 逻辑;

值和引用知识补充

如何判断值类型与引用类型

  • 使用 F12 查看源码定义:struct 是值类型,class 是引用类型。

语句块与变量作用域

  • 上层语句块:类、结构体;中层:函数;底层:条件分支/循环。
  • 成员变量在上层;临时变量在中、底层。
  • 代码逻辑主要写在函数、条件、循环中。

变量生命周期

  • 成员变量长期存活;临时变量随语句块结束回收。
  • 引用类型:栈上地址被释放,堆中数据变垃圾待 GC。
  • 减少循环内重复创建临时变量有助于性能。

使用高层变量记录底层变量

  • 用成员字段或静态变量记录函数内的中间值,避免被销毁。

结构体中的值类型与引用类型

  • 结构体本身是值类型,存在栈中。
  • 内部值类型存具体值;引用类型存地址,指向堆中对象。

类中的值类型与引用类型

  • 类本身是引用类型,存在堆中。
  • 值类型成员与类共存在堆;引用类型成员是指向另一个堆空间的地址。

数组存储规则

  • 数组是引用类型,栈上存地址。

    • 值类型数组:堆中存实际值。
    • 引用类型数组:堆中存的是其他堆地址(默认值为 null)。

结构体继承接口(装箱/拆箱)

  • 结构体实现接口会发生 装箱(复制到堆)。
  • 接口变量互赋共享地址,修改其中一个影响另一个。
  • 接口强转为结构体会发生 拆箱(复制回栈)。
  • 装箱带来性能开销,结构体应谨慎使用接口。


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

×

喜欢就点赞,疼爱就打赏