C#复习资料

核心目标: 理解原理、掌握应用、避开陷阱、应对提问。


一、 类型系统 (Type System) - 面试基石 & 高频考点

  1. 值类型 (Value Types) vs 引用类型 (Reference Types)

    • 本质区别:
      • 值类型 (struct, enum, 基本类型如 int, double, bool, char, decimal, DateTime):
        • 存储: 数据本身直接存储在变量位置(通常栈上,或嵌入在引用类型对象中)。
        • 赋值/传参: 复制整个数据副本。修改副本不影响原始值。
        • 继承: 隐式继承 System.ValueType (它继承 System.Object)。
        • 默认值: 所有字段为 0/false/null (取决于具体类型)。
        • 内存管理: 作用域结束时(栈上)或包含它的引用类型对象被回收时自动释放。无GC开销。
      • 引用类型 (class, interface, delegate, array, string):
        • 存储: 变量存储的是指向托管堆(Managed Heap)上对象内存地址的引用。对象本身在堆上。
        • 赋值/传参: 复制引用地址。多个变量可指向同一对象。通过任一引用修改对象,所有引用都“看到”修改。
        • 继承: 直接继承 System.Object 或其它类。
        • 默认值: null
        • 内存管理:垃圾回收器(GC) 自动管理。当对象不可达(Unreachable) 时会被回收。
    • string 的特殊性:
      • 引用类型,但因其不可变性(Immutability),行为有时像值类型。
      • 任何修改操作(Substring, Replace, += 等)都会在堆上创建新字符串对象。 (性能考量:大量字符串操作用 StringBuilder)。
      • 字符串驻留(String Interning): CLR 会维护一个驻留池,存储程序中声明的唯一字符串字面量。str1 = "hello"; str2 = "hello"; 可能指向同一个对象(但 new string("hello".ToCharArray()) 不会驻留)。String.Intern() / IsInterned()
    • 面试重点/易错点:
      • 值传递 vs 引用传递:理解 = 和传参时,值类型复制值,引用类型复制引用。
      • 修改影响:修改值类型成员(在类中)或通过引用修改引用类型对象内部状态的区别。
      • 性能:大型结构体频繁复制开销大;引用类型有GC开销。
      • string 连接性能陷阱:循环内大量 s += "a" 会产生大量中间垃圾字符串。务必用 StringBuilder
      • 何时用 struct 小(通常<16字节)、逻辑表示单一值、不可变、不常装箱。否则优先 class
  2. 装箱 (Boxing) 与拆箱 (Unboxing)

    • 装箱:值类型实例转换为引用类型对象 (object 或该值类型实现的接口类型)。在堆上分配内存,复制值类型数据到其中。
      • int i = 42; object o = i; // Boxing
    • 拆箱:装箱后的引用类型对象转换回原始值类型或其可空类型。需要显式类型转换
      • int j = (int)o; // Unboxing
    • 性能开销: 高! 涉及堆内存分配和复制。在性能敏感代码(如循环)中避免
    • 类型安全: 拆箱时必须确保目标类型精确匹配原始装箱类型,否则抛出 InvalidCastException
      • double d = 3.14; object o = d; int i = (int)o; // 运行时错误!
      • 正确做法:int i = (int)(double)o; 或直接 int i = (int)d; (避免装箱)
    • 面试重点: 解释概念、性能影响、如何避免(使用泛型集合 List 而非 ArrayList)、常见错误场景。
  3. 可空值类型 (Nullable Value Types)

    • 语法: NullableT? (如 int?, DateTime?)。
    • 用途: 允许值类型表示 null 值。常用于数据库交互、可选参数、表示缺失值。
    • 底层: 是一个轻量结构体 public struct Nullable where T : struct { public bool HasValue; public T Value; }
    • 访问:
      • 检查 HasValue 属性。
      • 安全获取 Value 属性 (若 HasValue == false, 访问 ValueInvalidOperationException)。
      • ?? 空合并运算符: 提供默认值。int result = nullableInt ?? -1;
      • ?. 空条件运算符 (C# 6+): 安全访问成员。int? length = customer?.Name?.Length; (任一环节为 null 则整个表达式结果为 null)。
    • 面试重点: 为什么需要?如何安全使用????. 的作用。
  4. 类型转换 (Type Conversion)

    • 隐式转换 (Implicit): 编译器自动进行,安全无信息丢失 (如 int -> long, short -> int, 派生类 -> 基类)。
    • 显式转换 (Explicit / Casting): 需程序员用 (type) 指定,可能丢失信息或失败 (如 long -> int, double -> int, object -> string, 基类 -> 派生类)。
    • as 运算符:
      • 用于引用类型可空值类型的安全转换。
      • 转换成功返回目标类型引用,失败返回 null (不抛出异常)。
      • string s = obj as string; if (s != null) { ... }
    • is 运算符:
      • 检查对象是否兼容于指定类型或实现某接口。
      • if (obj is string) { ... }
      • 模式匹配 (C# 7+): if (obj is string s) { /* 可直接使用变量 s */ } (同时引入新变量 s 并赋值)。
    • 面试重点: as vs (type) 强转 (失败时行为不同)、is 模式匹配的便利性、值类型转换的精度丢失问题。

二、 面向对象编程 (OOP) - 灵魂所在 & 设计基础

  1. 类 (Class) vs 结构体 (Struct)

    • 核心区别 (重温并强调):
      特性 类 (class) 结构体 (struct)
      类型 引用类型 值类型
      内存 堆 (Heap) 栈 (Stack) / 嵌入在包含对象中
      赋值 复制引用 (指向同一对象) 复制整个值 (新副本)
      继承 支持单继承 不支持继承 (只能实现接口)
      无参构造 可显式定义 (若有参构造则需显式) 不能显式定义 (编译器自动生成)
      字段初始化 无强制要求 必须在声明时或在构造中初始化所有字段
      适合场景 复杂对象、有行为、有状态、需继承 小型、轻量、数据为主、值语义、不可变
    • 面试重点: 为什么结构体赋值是复制? 性能影响?为什么结构体不能有显式无参构造? 何时选择 struct (强调值语义、不可变、小尺寸)。readonly struct (C# 7.2+) 的意义 (强制不可变,优化编译器)。
  2. 面向对象四大特性

    • 封装 (Encapsulation):
      • 核心: 将数据(字段)和操作数据的方法(行为)绑定在一个单元(类)中,并控制对内部实现的访问。
      • 访问修饰符 (Access Modifiers):
        • public: 任何地方可访问。
        • protected: 本类及其派生类可访问。
        • internal: 同一程序集(Assembly) 内可访问。默认的类访问级别。
        • protected internal: protected internal (取并集)。
        • private: 仅本类可访问 (默认的成员访问级别)。
      • 属性 (Property):
        • 封装字段访问的优雅方式,提供 get (读取) 和 set (写入) 访问器。
        • 自动属性 (Auto-Implemented Property): public string Name { get; set; } (编译器自动生成私有字段)。
        • 完整属性 (Full Property): 可添加逻辑 (验证、惰性加载、通知)。
          private string _name;
          public string Name
          {
              get => _name;
              set
              {
                  if (string.IsNullOrWhiteSpace(value))
                      throw new ArgumentException("Name cannot be empty");
                  _name = value;
              }
          }
          
        • 只读属性: public int Id { get; } (只能在构造函数或初始化器中设置)。
        • 计算属性: get 中返回计算值,无后备字段。
    • 继承 (Inheritance):
      • : 语法: class DerivedClass : BaseClass { ... }
      • 单继承: C# 只允许一个直接基类
      • base 关键字: 访问基类成员 (如调用基类构造函数 base(...), 调用基类方法 base.MethodName())。派生类构造函数必须 (显式或隐式) 调用基类构造函数!
      • sealed 关键字: 修饰类,阻止该类被继承;修饰方法 (override 的方法),阻止该方法在派生类中被进一步重写。
      • 构造顺序: 基类静态构造 -> 派生类静态构造 -> 基类实例构造 -> 派生类实例构造。析构/回收顺序相反。
    • 多态 (Polymorphism):
      • 核心: “一个接口,多种实现”。允许使用基类引用调用派生类对象的方法,执行派生类的实现。
      • 实现机制:
        • 虚方法 (virtual): 在基类中声明,表示该方法可以在派生类中被重写。
        • 重写 (override): 在派生类中使用,提供基类虚方法的新实现
        • new 修饰符 (隐藏): 在派生类中使用,声明一个与基类同名的成员 (非虚方法或未重写的虚方法)。这不是重写,只是隐藏基类成员。通过基类引用访问时,调用的是基类版本;通过派生类引用访问时,调用的是派生类新版本。尽量避免使用 new,易混淆。
        class Shape { public virtual void Draw() => Console.WriteLine("Shape"); }
        class Circle : Shape { public override void Draw() => Console.WriteLine("Circle"); } // 多态
        class Square : Shape { public new void Draw() => Console.WriteLine("Square"); }    // 隐藏
        Shape s1 = new Circle(); s1.Draw(); // Output: "Circle" (多态)
        Shape s2 = new Square(); s2.Draw(); // Output: "Shape" (基类方法,隐藏)
        Square sq = new Square(); sq.Draw(); // Output: "Square" (派生类新方法)
        
      • 抽象类 (abstract class) 和抽象方法 (abstract method):
        • 抽象类不能被实例化,只能被继承。
        • 抽象方法只有声明 (abstract + 签名),没有实现必须在非抽象派生类中被 override
        • 抽象类可以包含非抽象成员(字段、属性、有实现的方法)。
        • 用于定义公共契约部分实现,强制派生类提供特定功能。
      • 接口 (interface):
        • 定义一组契约 (方法、属性、事件、索引器的签名,不包含实现 - C# 8.0 前)。
        • 类或结构体通过 : 语法实现接口 (class MyClass : IMyInterface, IAnotherInterface)。
        • 多重实现: 一个类/结构体可以实现多个接口。
        • 接口成员默认是 public,不能有访问修饰符。
        • 显式接口实现 (Explicit Interface Implementation): 解决不同接口有同名方法的问题,或隐藏接口实现细节。void IInterface.Method() { ... }。通过接口引用调用。
        • C# 8.0+ 默认接口方法 (Default Interface Methods): 允许在接口中为方法提供默认实现。用于在不破坏现有实现的情况下扩展接口。
        • 接口 vs 抽象类:
          • 继承: 类单继承,接口多重实现。
          • 状态: 抽象类可以有字段(状态),接口不能(C# 8.0+ 可以有静态字段)。
          • 构造函数: 抽象类有构造函数,接口没有。
          • 版本控制: 接口添加新成员会破坏现有实现者(除非用默认实现);抽象类添加非抽象方法通常不会破坏。
          • 设计目的: 接口定义行为契约,抽象类提供部分实现和共性
    • 抽象 (Abstraction): 与封装紧密相关,关注于暴露必要功能,隐藏复杂内部细节。接口和抽象类是强力的抽象工具。方法签名也是一种抽象(隐藏实现)。
  3. 成员详解

    • 构造函数 (Constructor): public ClassName(...) { ... }。初始化对象状态。可重载。静态构造函数 (Static Constructor): static ClassName() { ... }。初始化静态成员,只执行一次,在首次访问类前自动调用。
    • 终结器 (Finalizer/Destructor): ~ClassName() { ... }。在 GC 回收对象内存之前调用。无法确定何时调用主要用于清理非托管资源(但不可靠!)现代代码优先使用 IDisposable 模式!
    • 参数传递:
      • 按值传递 (默认): 对于值类型,传递副本;对于引用类型,传递引用的副本(指向同一个对象)。方法内修改引用类型对象的状态会影响调用方。
      • ref 传递: void Method(ref int x). 传递变量的内存地址。方法内对参数 (x) 的修改直接影响调用方的原始变量。调用时需加 ref (Method(ref myVar);)ref 可用于值类型和引用类型。
      • out 传递: void Method(out int result). 用于返回多个值。方法内部必须在返回前为 out 参数赋值。调用时需加 out (Method(out var output);)。调用前变量无需初始化
      • in 传递 (C# 7.2+): void Method(in LargeStruct data). 传递对大型值类型的只读引用,避免复制开销。方法内部不能修改 in 参数。调用时通常无需加 in (编译器优化)。
    • 索引器 (Indexer): 使对象能像数组一样索引访问。public T this[int index] { get { ... } set { ... } }
    • 运算符重载 (Operator Overloading): public static Complex operator +(Complex a, Complex b) { ... }。重载 +, -, *, /, ==, !=, <, >, true, false 等。要求成对重载 (如 ==!=)谨慎使用,确保符合直觉

三、 内存管理与垃圾回收 (GC) - 性能关键 & 资源管理

  1. 垃圾回收器 (GC) 核心原理

    • 托管堆 (Managed Heap): CLR 管理的内存区域,引用类型对象分配于此。
    • GC 目标: 自动回收不再被应用程序使用的对象(垃圾)占用的内存。
    • 根 (Roots): 静态字段、局部变量/参数、CPU 寄存器、GC 句柄等。这些是应用程序直接可达的对象起点。
    • 标记 (Marking): 从根出发,遍历所有可达(Reachable) 的对象图,标记为“存活”。
    • 清除 (Sweeping): 回收所有未被标记(不可达)的对象的内存。
    • 压缩 (Compacting): (主要发生在 Gen 2) 移动存活对象,消除内存碎片,使空闲内存连续。大对象堆(LOH)通常不压缩。
    • 分代 (Generations): 提高效率。假设“对象越新,越可能被回收;对象越老,越可能存活”。
      • Gen 0 (第0代): 新创建的小对象。回收最频繁,最快(只回收 Gen 0)。
      • Gen 1 (第1代): 从 Gen 0 GC 中幸存下来的对象。回收频率较低(回收 Gen 0 + Gen 1)。
      • Gen 2 (第2代): 长期存活的对象。回收频率最低,耗时最长(回收所有代)。Full GC
      • 大对象堆 (Large Object Heap - LOH): 存放大对象 (通常 >= 85KB)。分配在特殊区域,回收策略不同(主要在 Gen 2 GC 时回收),不易压缩。频繁分配/回收大对象可能导致 LOH 碎片。
    • GC 触发条件: Gen 0 满、显式调用 GC.Collect() (强烈不建议在生产代码中随意调用!)、系统内存不足、AppDomain 卸载、CLR 关闭等。
    • GC 是非确定性的: 无法精确预测何时发生。
  2. IDisposable 接口与 using 语句 - 管理非托管资源

    • 核心问题: GC 只管理内存,不管理非托管资源 (文件句柄、数据库连接、网络套接字、GDI 对象、COM 对象等)。这些资源必须显式、及时释放。
    • IDisposable 接口: 定义了 Dispose() 方法,用于释放非托管资源 (也可用于释放托管资源)。
    • 标准 Dispose 模式:
      public class ResourceHolder : IDisposable
      {
          // 标记是否已释放
          private bool _disposed = false;
          // 托管资源
          private SomeManagedResource _managedResource;
          // 非托管资源 (通常用 IntPtr 句柄表示)
          private IntPtr _nativeHandle;
      
          // 实现 IDisposable
          public void Dispose()
          {
              Dispose(true); // 释放托管和非托管资源
              GC.SuppressFinalize(this); // 告诉GC不需要再调用终结器
          }
          // 受保护的虚方法,实际释放逻辑
          protected virtual void Dispose(bool disposing)
          {
              if (!_disposed)
              {
                  if (disposing)
                  {
                      // 释放托管资源 (它们也可能实现 IDisposable)
                      _managedResource?.Dispose();
                  }
                  // 释放非托管资源
                  CloseHandle(_nativeHandle); // 调用 Win32 API 或其他释放方法
                  _nativeHandle = IntPtr.Zero;
                  _disposed = true;
              }
          }
          // 终结器 (安全网,防止忘记调用 Dispose)
          ~ResourceHolder()
          {
              Dispose(false); // 只释放非托管资源
          }
          // 其他方法需检查 _disposed 状态,抛出 ObjectDisposedException
      }
      
    • using 语句: 确保 Dispose() 被调用的语法糖,即使块内发生异常。
      using (var resource = new ResourceHolder())
      {
          // 使用 resource
      } // 此处自动调用 resource.Dispose()
      // C# 8+ 简洁写法 (无需大括号)
      using var resource = new ResourceHolder();
      // ... 作用域结束时自动 Dispose
      
    • 面试重点:
      • 为什么需要 IDisposable (GC 不管非托管资源)。
      • using 语句的作用和原理? (try/finally 的包装)。
      • Dispose 模式中 disposing 参数的作用? (区分是 Dispose() 调用还是终结器调用)。
      • GC.SuppressFinalize(this) 的作用? (避免对象进入终结队列,提升性能)。
      • 终结器 (~) 的作用和局限性? (安全网,但调用时机不确定,不能依赖)。

四、 关键特性与机制 - 高频面试点

  1. 委托 (Delegate) 与事件 (Event)

    • 委托 (Delegate): 类型安全的函数指针。定义了方法的签名(参数类型和返回类型)。
      • 声明: delegate void MyDelegate(string message);
      • 实例化: MyDelegate del = new MyDelegate(MyMethod);MyDelegate del = MyMethod; (简写)。
      • 调用: del("Hello");del?.Invoke("Hello"); (空安全)。
      • 多播委托 (Multicast Delegate): 委托可以组合 (+, +=) 和移除 (-, -=)。调用时按添加顺序依次调用所有方法。返回值是最后一个方法的返回值。
    • 事件 (Event): 基于委托,实现发布-订阅(Publish-Subscribe) 模型。提供对委托的封装,控制外部访问 (主要是防止外部直接 = 覆盖)。
      • 声明: public event EventHandler MyEvent; (EventHandler 是常用预定义委托 delegate void EventHandler(object sender, EventArgs e);)。
      • 订阅: obj.MyEvent += HandleEvent;
      • 取消订阅: obj.MyEvent -= HandleEvent; (重要!避免内存泄漏!)
      • 触发: MyEvent?.Invoke(this, EventArgs.Empty); (空安全)。
    • 面试重点:
      • 委托是什么?解决了什么问题? (解耦调用者和具体方法)。
      • 事件和委托的关系? (事件是委托的封装器,提供更安全的订阅模型)。
      • EventHandlerEventArgs 的作用? (标准事件模式)。
      • 为什么需要取消订阅事件? (防止事件源持有订阅者引用导致订阅者无法被GC回收 - 内存泄漏根源!)。
  2. 集合 (Collections)

    • 核心接口:
      • IEnumerable / IEnumerable: 定义 GetEnumerator(),支持 foreach 遍历。
      • ICollection / ICollection: 继承 IEnumerable,添加计数 (Count)、添加(Add)、移除(Remove)、清空(Clear)等。
      • IList / IList: 继承 ICollection,添加索引访问 ([index])、插入(Insert)、移除(RemoveAt)。
      • IDictionary / IDictionary: 存储键值对,按键访问 ([key]), Keys, Values
    • 常用具体类:
      • List: 动态数组。索引访问快(O(1)),中间插入/删除慢(O(n))。最常用。
      • Dictionary: 哈希表。按键查找/插入/删除快(接近 O(1))。键必须唯一。按键查找首选。
      • HashSet: 无序唯一值集合。基于哈希。判断存在性(Contains)、添加(Add)、集合运算(并、交、差)快。去重、集合运算首选。
      • Queue: FIFO (先进先出) 队列。Enqueue, Dequeue, Peek
      • Stack: LIFO (后进先出) 栈。Push, Pop, Peek
      • LinkedList: 双向链表。任意位置插入/删除快(O(1)),索引访问慢(O(n))。AddFirst, AddLast, AddBefore, AddAfter, Remove
    • 面试重点:
      • List vs Array (List 动态扩容)。
      • Dictionary 的工作原理? (哈希表、哈希冲突解决 - 通常链表法)。
      • 如何选择集合类型? (根据访问模式:索引、按键、顺序、唯一性、插入位置)。
      • foreach 遍历时修改集合会怎样? (通常抛出 InvalidOperationException)。如何安全修改? (遍历时记录要修改的元素,遍历完再处理;或使用 for 循环从后往前删除;或用 ToList() 创建副本遍历)。
      • 自定义类型作为 Dictionary 的键需要做什么? (正确重写 GetHashCode()Equals(object) / Equals(T))。GetHashCode() 规则: 相等对象必须返回相同哈希码;尽量分布均匀;基于不可变字段计算。Equals 规则: 自反、对称、传递、一致、非空。
  3. 异常处理 (Exception Handling)

    • try-catch-finally
      • try: 包含可能抛出异常的代码。
      • catch: 捕获并处理特定类型或更通用的异常。应优先捕获更具体的异常类型。
      • finally: 无论是否发生异常都会执行。用于释放资源(即使 catch 块中有 return 或重新抛出异常)。
    • 抛出异常: throw 关键字。throw new MyException("message"); / throw; (重新抛出当前异常,保留原始堆栈跟踪)。
    • 自定义异常: 继承自 Exception 或其子类 (如 ApplicationException)。提供有意义的错误信息和上下文。
      public class MyAppException : Exception
      {
          public int ErrorCode { get; }
          public MyAppException(string message, int errorCode) : base(message)
          {
              ErrorCode = errorCode;
          }
          public MyAppException(string message, int errorCode, Exception innerException)
              : base(message, innerException)
          {
              ErrorCode = errorCode;
          }
      }
      
    • 最佳实践:
      • 只在异常情况下使用异常! 不要用异常控制常规流程 (如解析失败用 TryParse 而非捕获异常)。
      • 提供清晰且有意义的错误信息。
      • 捕获你能处理的异常。 不要“吞噬”异常 (catch {}catch (Exception ex) { } 不做任何处理/记录)。
      • finallyusing 中释放资源。
      • 考虑使用 try-catch 包装跨越信任边界(如调用外部服务、读取用户输入)的代码。
      • 使用多个 catch 块时,从最具体到最通用 (Exception) 排序。
    • 面试重点:
      • finally 块何时执行? (即使有 return 或异常抛出)。
      • throw ex;throw; 的区别? (throw ex; 重置堆栈跟踪为当前点,throw; 保留原始堆栈跟踪)。
      • 为什么不要捕获 Exception (可能掩盖你无法处理或未预料到的严重错误)。
      • 如何设计自定义异常?
  4. async/await (异步编程)

    • 目的: 编写非阻塞代码,提高应用程序的响应性吞吐量 (尤其I/O密集型操作)。
    • 关键字:
      • async: 修饰方法,表示该方法包含异步操作。方法通常返回 Task, TaskValueTask/ValueTask
      • await: 用在 async 方法内部,等待一个 Task (或其他 awaitable 类型) 完成。不会阻塞调用线程! 方法在此处“暂停”,控制权返回给调用者。当 Task 完成时,方法从此处恢复执行(可能在原线程,也可能在另一个线程池线程)。
    • 返回类型:
      • Task: 表示一个没有返回值的异步操作。
      • Task: 表示一个返回 TResult 类型值的异步操作。
      • ValueTask / ValueTask: Task 的轻量级替代,适合可能同步完成的操作,避免堆分配开销。
      • void: 仅用于事件处理程序。调用者无法 await 或捕获异常。尽量避免在其他地方使用。
    • 底层原理: 状态机。编译器将 async 方法重写为一个状态机类,管理 await 点的暂停和恢复。
    • 最佳实践:
      • 避免 async void (除事件处理器外)。
      • 始终 await 返回 Task 的方法 (除非在顶级或 Main 方法中,此时可 .Wait()/.Result - 慎用,可能导致死锁!)。
      • 使用 ConfigureAwait(false):在库代码或无 UI 上下文中,告诉运行时恢复时不强制要求回到原始同步上下文,可提高性能避免死锁。
      • 处理异常:async 方法内部用 try/catch。异常会存储在返回的 Task 对象中,在 awaitTask 时抛出。
      • 避免 Task.Run 包装 CPU 密集型同步代码来“伪异步”。真异步基于 I/O 完成端口等机制。
    • 面试重点:
      • async/await 如何提升性能? (释放线程做其他事,非阻塞)。
      • await 会阻塞线程吗? (不会!)。
      • async void 有什么问题? (调用者无法追踪状态、无法捕获异常)。
      • 死锁场景? (在 UI 线程或 ASP.NET Core 前身请求上下文中,同步等待 .Result/.Wait() 一个需要该上下文恢复的 Task)。如何避免? (库代码用 ConfigureAwait(false);应用层避免同步等待异步方法,坚持 async 全链路)。
      • Task vs ValueTask (何时选用后者?)。

五、 其他重要概念 & 新特性 (C# 6.0+)

  1. var 关键字: 局部变量类型推断。var list = new List();编译时确定类型,是语法糖。不影响运行时性能。 在类型明显时增强可读性。
  2. nameof 运算符: throw new ArgumentNullException(nameof(param));。获取变量、类型或成员的字符串名称。重构友好。
  3. 空条件运算符 ?. / ?[] customer?.Address?.City / list?[0]。安全导航,避免 NullReferenceException。任一环节为 null 则返回 null
  4. 空合并运算符 ?? / ??= string name = inputName ?? "Default"; / list ??= new List();。提供默认值或空时赋值。
  5. 字符串插值 ($): $"Hello, {name}!"。比 string.Format 更简洁高效。
  6. 属性初始化器: public string Name { get; set; } = "Unknown";
  7. 表达式体成员 (Expression-bodied members): public string FullName => $"{FirstName} {LastName}"; / public void Print() => Console.WriteLine(FullName);。简化单行方法/属性/索引器/构造/终结器。
  8. 元组 (Tuples) & 解构:
    • (string Name, int Age) person = ("Alice", 30); / var person = (Name: "Alice", Age: 30);
    • Console.WriteLine($"{person.Name}, {person.Age}");
    • 解构: (string name, int age) = person; / var (name, age) = person;
  9. 模式匹配 (C# 7.0+ 持续增强):
    • is 类型模式:if (obj is string s) { ... }
    • switch 表达式和模式:
      string result = shape switch
      {
          Circle c => $"Circle with radius {c.Radius}",
          Rectangle r when r.Width == r.Height => $"Square with side {r.Width}",
          Rectangle r => $"Rectangle {r.Width}x{r.Height}",
          _ => "Unknown shape"
      };
      
    • 属性模式、元组模式等。
  10. 记录类型 (Record Types) (C# 9.0+):
    • public record Person(string FirstName, string LastName); (简洁定义不可变数据载体)。
    • 编译器自动生成:基于值的 Equals/GetHashCodeToStringwith 表达式 (非破坏性修改 var newPerson = person with { LastName = "Smith" };)。
    • 支持继承。
  11. 顶级语句 (Top-level statements) (C# 9.0+): 简化小型程序入口点。Console.WriteLine("Hello World!");
  12. 全局 using 指令 (C# 10.0+): global using System; 在全局文件中声明,整个项目有效。
  13. 文件范围的命名空间 (File-scoped namespaces) (C# 10.0+): namespace MyNamespace; (文件内所有代码都在此命名空间)。

你可能感兴趣的:(c#,java,jvm)