速览 · A

语法速览:一章过完 C#

这一章不深挖任何一条差异,只做一件事:把 C# 的语法面从上到下、一次性扫完。

你可以把它当成"全书的地图"——先知道 C# 里有什么,再回到后面的核心章节去理解为什么和 C++ 不一样。 也可以把它当成"速查手册",写代码时回来翻一翻。

这一章的作用
  • 给你一张完整的 C# 语法结构图,不再有"某个东西我不知道它存在"的盲区。
  • 用表格把 C# 的类型、运算符、修饰符、关键字一次性过一遍,并标注和 C++ 的差异点。
  • 每个语法点只给"最小可运行示例",深挖交给后面的章节(会标出"详见第几章")。
🔑 怎么读这一章

第一遍:只扫表和代码骨架,不求记住。重点是建立"C# 里长什么样"的整体印象。
第二遍:读完之后回到这里,逐条自问"这个我会不会写"。不会的就去对应章节。
每一节末尾都有 → 详见 指向深入章节。

A.1 程序结构:一个 .cs 文件长什么样

一个 .cs 文件从上到下的层次 using System; // 引入命名空间 namespace MyApp; // 命名空间(文件作用域) class Program { // 类型(类 / 结构 / 接口…) int count = 0; // 字段 void Do() { ... } // 方法 static void Main() { } // 入口(或改用顶级语句) } 层次:程序集 → 命名空间 → 类型 → 成员
没有头文件、没有 #include、没有前置声明——一个文件既是"声明"也是"实现"。
C++ · 传统骨架
#include <iostream>      // 文本包含
    #include "myclass.h"     // 必须手动引入

    int main() {              // 入口是全局函数
        std::cout << "hi";
        return 0;
    }
C# · 两种写法
// 写法一:顶级语句(新项目默认,最简)
    Console.WriteLine("hi");
    // 编译器自动生成 Main 和 class Program

    // 写法二:传统显式结构
    using System;
    namespace MyApp;
    class Program {
        static void Main(string[] args) { }
    }
概念C++C#
引入其它代码#include 头文件同一项目自动可见;外部用"引用程序集"
命名空间namespace A { namespace B { } }namespace A.B;(文件作用域,只用一次)
入口点全局 int main()static void Main(string[]) 或顶级语句
语句结束 / 块; 和 { }一样
大小写敏感敏感
注释// /* */一样,另有 /// XML 文档注释
预处理宏、条件编译、include只剩 #if/#region/#nullable 等,没有宏

→ 详见 第 02 章 · 编译与运行模型

A.2 类型系统速查

整数:大小固定是最大的不同

C++ 里 int/long 的大小随平台和编译器变化;C# 的整数类型位数是写死的,跨平台一致。

C# 类型位数 / 范围.NET 全名字面量后缀C++ 对应
sbyte8 位有符号 −128~127SByte—signed char / int8_t
byte8 位无符号 0~255Byte—unsigned char / uint8_t
short16 位 ±3.2 万Int16—short
int ★32 位 ±21 亿Int32—int(但大小不保证 32 位)
long64 位Int64Llong long
uint / ulong32 / 64 位无符号UInt32 / UInt64U / ULunsigned
nint / nuint与指针同宽(64 位机上 64 位)IntPtr—intptr_t
char16 位,存 UTF-16 码元Char'a'char 通常是 8 位(注意差异)
⚠️ 两个必须记住的整数差异

① C# 的 int 永远是 32 位,不会因为平台变成 64 位。(C++ 的 long 在 Windows 是 32 位、Linux 是 64 位,这种坑 C# 没有。)
② C# 的 char 是 2 字节的 UTF-16 码元,不是 1 字节。所以 "abc".Length 和"字节数"不是一回事,中文一个字通常占 1 个 char。

浮点与小数

C# 类型位数 / 精度后缀C++ 对应
float32 位,约 7 位有效数字ffloat
double ★64 位,约 15-16 位有效数字(小数默认类型)无 / ddouble
decimal128 位,约 28-29 位,专为钱m没有对应物(C++ 里一般用定点或整数分)
Half16 位半精度—无
C# · 小数与钱
double d = 3.14;          // 小数默认是 double
    float  f = 3.14f;         // float 必须加 f
    decimal m = 19.99m;       // 钱用 decimal,必须加 m

    Console.WriteLine(0.1 + 0.2);        // 0.30000000000000004
    Console.WriteLine(0.1m + 0.2m);      // 0.3  ← decimal 精确

其它基本类型

C# 类型说明C++ 对应
bool只有 true / false,不能和整数互转bool(但 C++ 可隐式转 int,C# 不行)
string引用类型、不可变、UTF-16std::string(可变)
object所有类型的根void* / std::any 的意思,但类型安全
dynamic关闭编译期类型检查,运行期绑定无直接对应(近似 std::any + 反射)
var只是推断,仍是强类型auto

复合类型与可空

C# · 复合类型一览
int[] arr = { 1, 2, 3 };              // 数组(引用类型,带边界检查)
    int[,] grid = new int[2, 3];            // 二维数组
    int[][] jag = new int[3][];              // 交错数组
    List<int> list = new();                // 泛型集合
    (int a, string b) t = (1, "x");         // 元组
    var (x, y) = t;                          // 解构

    int? maybe = null;                      // 可空值类型(Nullable<int>)
    string? s = null;                        // 可空引用类型(C# 8+,编译期警告)
    int v = maybe ?? -1;                    // ?? 提供默认值

默认值(C# 里字段一定有初值)

类型默认值C++ 里
所有数值类型0未初始化是垃圾值
boolfalse垃圾值
char'\0'垃圾值
引用类型 / string / 数组null未初始化指针
struct 字段逐字段取各自默认值未初始化
可空类型 int?null—
🔑 两条硬规则

① 类的字段不用你初始化,编译器保证有默认值(C++ 里是垃圾值——C# 消除了这个坑)。
② 局部变量必须先赋值才能用,否则编译不过(叫"明确赋值"检查)。所以 C# 里不会出现"用了未初始化的变量"。

→ 详见 第 04 章 · 值类型 vs 引用类型、第 08 章 · 字符串、集合、LINQ

A.3 变量、常量与字面量

C# · 声明与初始化
int a;                      // 局部变量:声明后必须赋值才能用
    a = 1;
    int b = 2;
    int c = 3, d = 4;             // 可以一行声明多个
    var e = 5;                  // 推断为 int(等价于 int e = 5)
    List<int> g = new();        // 目标类型 new(右侧可省类型)

    // 常量与只读
    const double Pi = 3.14159;      // 编译期常量,必须在声明处赋值
    readonly int _id;                // 运行期只读,可在构造函数里赋值
    static readonly DateTime Start = DateTime.Now;   // 常用的"运行时静态常量"

    // 字面量小技巧
    int big = 1_000_000;          // 下划线分隔(可读性)
    int hex = 0xFF;               // 十六进制
    int bin = 0b1010;             // 二进制
    long l = 9999999999L;         // 超出 int 要加 L
写法含义C++ 里
const编译期常量,只能是基本类型 / string / 枚举constexpr;C++ 的 const 语义更宽
readonly运行期只读字段,可在构造函数里赋值const 成员 / 初始化列表
static readonly"运行时才知道的常量"静态成员 + 初始化
var类型推断(编译期确定,仍是强类型)auto

字符串字面量

C# · 五种字符串写法
string s1 = "普通\t字符串\n";          // 有转义
    string s2 = @"C:\temp\new";              // 逐字字符串:反斜杠不转义
    string s3 = $"我是 {name},{age} 岁";      // 插值
    string s4 = $@"第 {n} 行:C:\temp";        // 插值 + 逐字
    string s5 = """ 原样 "引号" 也不怕 """;    // 原始字符串字面量(C# 11)

    char ch = 'A';                     // 单引号 = char(不是 string!)
    string one = "A";                    // 双引号 = string
⚠️ C++ 转过来最容易错的一点

C# 里 'A'(char)和 "A"(string)是两个完全不同的类型,不能混用;'A' 也不能隐式转成 int。 C++ 里 char 和 int 可以随意互转,这个习惯带过来会编译报错。

A.4 运算符全表

类别C# 运算符说明 / 与 C++ 的差异
算术+ - * / % ++ --同 C++。注意整数除法仍然截断
比较== != < > <= >=同符号,但 == 对引用类型是比引用(见第 04 章)
逻辑&& || !同 C++;没有 and/or/not 替代写法(C++ 有)
位运算& | ^ ~ << >> >>>>>> 是无符号右移(C# 11,C++ 没有)
赋值= += -= *= /= %= &= |= ^= <<= >>= ??=??= 是 C# 独有(左值为 null 才赋值)
三元cond ? a : b同 C++
空值?? ?. ?[] ?.Invoke() ?[i]C# 独有的一族"空安全"运算符
类型测试is / as / typeof / sizeof / nameof替代 dynamic_cast / typeid / sizeof / 手写字符串
范围 / 索引a[^1](倒数第一)a[1..3](切片)C# 独有
Lambda=>对应 C++ 的 [](){}
溢出控制checked / unchecked默认不检查整数溢出(和 C++ 一样),可显式开启
指针(unsafe)&x / *p / p->M只在 unsafe 块里可用(见第 07 章)
C# · 最值得先记住的四个新运算符
// ① ?? :左值若为 null,取右值(给默认值)
    string name = input ?? "(匿名)";

    // ② ?. :左值若为 null,整个表达式为 null(不抛异常)
    int? len = text?.Length;              // text 是 null → len 是 null
    var city = user?.Address?.City;       // 可以链式一路判空

    // ③ ??= :只在为 null 时赋值(惰性初始化)
    _cache ??= BuildCache();

    // ④ is :类型判断 + 顺便取值
    if (obj is Circle c && c.R > 10) { }
    if (x is not null) { }
    if (n is > 0 and < 100) { }        // 关系模式(第 11 章)

A.5 控制流

语句C++ 里有吗备注
if / else有条件必须是 bool,不能写 if (ptr)
switch(语句)有每个 case 必须 break(不能像 C++ 那样"贯穿")
switch(表达式)无var s = x switch { ... }; —— 直接返回值
for有语法一致
foreach有(range-for)foreach (var x in coll),任何可枚举对象都能用
while / do-while有一致
break / continue / return有一致
goto有存在,但几乎不用(goto case 偶尔用)
using无(靠 RAII)离开作用域自动 Dispose()(见第 09 章)
lock无(靠 RAII)lock (obj) { } 自动加解锁
yield return无写迭代器 / 惰性序列(IEnumerable)
await无(C++20 协程很不同)异步等待(见 A.10)
C++ · switch
switch (n) {
        case 1:
        case 2:
            Do();      // 1、2 都走这里(贯穿)
            break;
        default: break;
    }
C# · switch 语句 / 表达式
switch (n) {
        case 1:
        case 2:
            Do(); break;    // 多个 case 共享,需 break
        default: break;
    }

    // 表达式版:直接产出值
    string s = n switch {
        1  => "一",
        2  => "二",
        _  => "其它"     // _ 是兜底
    };
C# · foreach 与迭代器
// foreach:任何实现了 IEnumerable 的对象都能遍历
    foreach (var item in collection) { }

    // 带索引(C# 7+ 的元组解构写法)
    foreach (var (i, item) in list.Select((v, i) => (i, v))) { }

    // 自己写一个"惰性序列":yield return
    IEnumerable<int> Fib() {
        int a = 0, b = 1;
        while (true) {
            yield return a;
            (a, b) = (b, a + b);
        }
    }

A.6 方法与参数

C# · 方法的各种写法
// 普通方法
    public int Add(int a, int b) { return a + b; }

    // 表达式体(一行方法)
    public int Add2(int a, int b) => a + b;

    // 默认参数(C++ 也有)
    void Log(string msg, int level = 0) { }

    // 命名实参(C# 独有,调用时可读性极强)
    Log(level: 2, msg: "hi");

    // params:可变参数(C++ 是 ... 或 initializer_list)
    int Sum(params int[] nums) => nums.Sum();
    Sum(1, 2, 3, 4);                 // 直接展开传

    // ref / out / in:见第 07 章
    void Inc(ref int x) => x++;
    bool TryGet(string k, out int v) { v = 0; return false; }
    double Dist(in BigStruct a) => 0;

    // 局部函数(方法里再定义方法)
    void Outer() {
        int Inner(int x) => x * 2;
        Console.WriteLine(Inner(3));
    }

    // 扩展方法:给"别人的类型"加方法(C++ 里做不到)
    public static class StringExt {
        public static bool IsEmail(this string s) => s.Contains('@');
    }
    // 用法:"abc@x.com".IsEmail()  ← 像自带的方法一样
修饰符含义C++ 对应
static属于类型而非实例同
virtual可被重写(必须显式写)virtual
override重写基类虚方法(必须显式写)override(C++ 里可选)
abstract无实现,子类必须实现= 0 纯虚
sealed封死,不能再被重写 / 继承final(C++11)
new隐藏基类同名成员(不是重写)无(C++ 里就是普通同名函数)
partial允许把类型拆到多个文件无
async方法内含 await无(C++20 协程不同)
unsafe允许裸指针—(C++ 到处都行)
extern声明实现在外部(P/Invoke)extern 链接

A.7 类与成员速查

C# · 一个类里能有什么
public class Sample : IDisposable {
        // ① 常量 / 静态字段 / 字段
        public const int Max = 100;
        private static int _counter;
        private readonly string _name;

        // ② 属性(C# 的招牌)
        public int Id { get; set; }                 // 自动属性
        public string Name => _name;                  // 只读计算属性
        public string City { get; init; } = "上海";   // init:只在创建时能赋值
        public static int Count => _counter;          // 静态属性

        // ③ 索引器:让对象支持 []
        public int this[int i] { get => i; set { } }

        // ④ 构造函数 / 静态构造函数 / 终结器
        public Sample(string name) { _name = name; _counter++; }
        static Sample() { _counter = 0; }    // 只执行一次的静态初始化
        ~Sample() { }                         // 终结器(≠ C++ 析构函数!见第 03 章)

        // ⑤ 方法 / 虚方法
        public void Run() { }
        public virtual void Step() { }

        // ⑥ 事件与委托成员
        public event Action? Changed;
        public Func<int, int>? Transform;

        // ⑦ 运算符重载 / 转换运算符
        public static Sample operator +(Sample a, Sample b) => a;
        public static implicit operator string(Sample s) => s._name;

        // ⑧ 嵌套类型
        public enum Kind { A, B }

        // ⑨ 实现接口
        public void Dispose() { }
    }
类型关键字的种类C#特点
类class引用类型,单继承,可多实现接口
结构struct值类型,不能继承,适合小数据
记录record / record struct值语义的类,自动生成 Equals / 哈希 / ToString
接口interface只约定能力,可实现多个
枚举enum底层是整数,有 Enum.GetValues 等工具
委托类型delegate类型安全的函数签名
静态类static class只能有静态成员,用来放工具方法
分部partial class/struct/interface拆成多个文件,编译时合并
抽象 / 密封abstract class / sealed class不能实例化 / 不能被继承
访问级别可见范围C++ 对应
public任何地方同
private仅本类(成员默认值)private
protected本类 + 派生类protected
internal本程序集(本项目)内无(近似匿名命名空间)
protected internal派生类 或 同程序集无
private protected派生类 且 同程序集无

→ 详见 第 05 章 · 面向对象差异、第 11 章 · 现代 C# 特性

A.8 继承 · 多态 · 接口

C# · 继承与多态的最小完整示例
public interface IShape {                 // 接口:只声明能力
        double Area();
        string Name { get; }
    }

    public abstract class Shape : IShape {   // 抽象类:唯一基类,可给部分实现
        public abstract double Area();          // 子类必须实现
        public virtual string Name => "形状";      // 子类可选重写
    }

    public sealed class Circle : Shape {      // sealed:到此为止,不许再继承
        public double R { get; }
        public Circle(double r) => R = r;
        public override double Area() => Math.PI * R * R;
        public override string Name => "圆";
    }

    // 多态:基类引用指向派生类对象
    Shape s = new Circle(2);
    Console.WriteLine(s.Area());     // 走 Circle 的实现

    // 类型判断与转换
    if (s is Circle c) { }          // 安全:是才转
    Circle? c2 = s as Circle;           // 不是则为 null
    Circle c3 = (Circle)s;             // 强制转,失败抛异常
特性C#C++
类多继承不支持支持
接口多实现支持用纯虚类模拟
默认虚方法不虚,要写 virtual不虚,要写 virtual(一样)
重写时必须写 override可写可不写
隐藏成员必须写 new无此概念
基类构造调用: base(args)初始化列表
所有类型的根object无
接口默认实现C# 8+ 支持纯虚类可带实现

A.9 泛型 · 委托 · Lambda · LINQ

C# · 泛型
// 泛型类
    public class Box<T> {
        public T Value { get; set; }
    }

    // 泛型方法 + 约束
    T Max<T>(T a, T b) where T : IComparable<T>
        => a.CompareTo(b) > 0 ? a : b;

    // 常用约束:where T : struct / class / new() / Base / IFoo / notnull / unmanaged
C# · 委托、Lambda、事件
// 自定义委托类型
    public delegate int Calculator(int a, int b);

    // 内置的(日常几乎只用这三个)
    Action<int> log = x => Console.WriteLine(x);      // 无返回值
    Func<int, int, int> add = (a, b) => a + b;      // 最后一个是返回类型
    Predicate<int> isPos = x => x > 0;              // 返回 bool

    // 多播:一个委托串多个方法(C# 独有)
    log += x => Console.WriteLine($"第二个:{x}");
    log -= log;                                       // 取消挂载

    // 事件:对外只能 += / -=
    public event EventHandler? Clicked;
    Clicked?.Invoke(this, EventArgs.Empty);
C# · LINQ:一句话筛 / 排 / 分组 / 聚合
using System.Linq;

    var result = people
        .Where(p => p.Age >= 18)              // 筛选
        .OrderByDescending(p => p.Age)        // 排序
        .Select(p => p.Name)                  // 投影
        .ToList();                            // 立刻执行,得到 List

    // 聚合与判断
    int total = nums.Sum();
    double avg = nums.Average();
    bool anyBig = nums.Any(n => n > 100);
    var first = people.FirstOrDefault(p => p.Age > 60);

    // 分组
    var groups = people.GroupBy(p => p.City);

    // 查询语法(等价写法,处理 join 时更好读)
    var q = from p in people
            where p.Age > 18
            orderby p.Age descending
            select p.Name;
🔑 记住"延迟执行"

Where / Select / OrderBy 不会立刻计算, 只是搭了一条"管道";真正遍历它(foreach、ToList()、Count())时才执行。 想固定结果就加 .ToList()。

→ 详见 第 06 章 · 泛型 vs 模板、第 08 章 · 字符串、集合、LINQ、第 10 章 · 委托、事件、Lambda

A.10 异常 · 资源 · 异步

C# · 异常处理
try {
        RiskyWork();
    }
    catch (FileNotFoundException ex) { Console.WriteLine(ex.Message); }
    catch (Exception ex) when (ex.HResult != 0) { throw; }   // when = 异常筛选
    finally { Cleanup(); }                                  // 一定执行

    // 自定义异常
    public class MyException : Exception {
        public MyException(string msg, Exception? inner = null) : base(msg, inner) { }
    }

    // 关键:重新抛出用 throw;(不要 throw ex;)
    catch (Exception ex) { Log(ex); throw; }
C# · 资源:using 就是 C# 的 RAII
using var reader = new StreamReader("a.txt");   // 作用域结束自动 Dispose()
    string line = reader.ReadLine();

    using (Stream s = ..., StreamReader r = new(s)) { }   // 也能一次写多个

    lock (_gate) { }        // 自动加锁 / 解锁,相当于 C++ 的 lock_guard

    // 自己写资源类:实现 IDisposable
    public void Dispose() { /* 释放句柄、连接等 */ GC.SuppressFinalize(this); }
C# · 异步:async / await
public async Task<string> FetchAsync(string url) {
        using var http = new HttpClient();
        string body = await http.GetStringAsync(url);   // 不阻塞线程
        return body;
    }

    // 并行等多个
    string[] all = await Task.WhenAll(urls.Select(FetchAsync));
    await Task.Delay(1000);                       // 异步延时

    // 返回值:Task(无返回)/ Task<T>(有返回)/ ValueTask<T>(高频小结果)
    // 三不要:不要 .Result / 不要 .Wait() / 不要 async void(除事件处理器)

→ 详见 第 09 章 · 异常与资源管理、第 11 章 · 现代 C# 特性

A.11 命名规范(和 C++ 差别很大)

成员C# 惯例常见 C++ 写法
类 / 结构 / 接口 / 记录PascalCase;接口加 I 前缀PascalCase / snake_case;接口常见 Shape
方法 / 属性 / 事件PascalCasePascalCase 或 snake_case
局部变量 / 参数camelCasesnake_case 很常见
私有字段_camelCase(下划线开头)m_ 前缀 / name_ 后缀
常量PascalCase(不是全大写)MAX_SIZE
泛型参数T、TKey、TValueT、Key
命名空间PascalCase.Point.Separated常见全小写 foo::bar
⚠️ 三个最容易改不过来的习惯

① 方法名首字母大写:calculateTotal() → CalculateTotal()。
② 常量不要全大写:MAX_SIZE → MaxSize。
③ 长度是属性不是方法:s.size() → s.Length;list.size() → list.Count(不带括号)。

A.12 关键字总表(可筛选)

下面这张表把 C# 的关键字按用途归类。在输入框里打字可以实时筛选——比如输入 ref 或 async,只留下相关行。

共 60 条

关键字作用C++ 里对应 / 差异
namespace组织类型、避免重名namespace(可用文件作用域写法)
using① 引入命名空间 ② 自动释放资源① 近似 using namespace;② RAII(C# 独有)
class引用类型class
struct值类型,不可继承struct(但 C++ 的 struct 是类,不是值类型)
record值语义数据类,自动生成比较 / 哈希 / 打印无(要手写 operator==)
interface能力约定,可多实现纯虚类
enum具名整数常量组enum class
delegate定义函数签名的类型函数指针 / std::function(但委托是对象)
event受保护的多播委托无
partial把类型拆到多个文件无
public公开public
private仅本类(成员默认)private
protected本类 + 派生类protected
internal同程序集可见无对应(近似匿名命名空间)
static属于类型本身static
readonly运行期只读字段 / 只读结构成员const 成员
const编译期常量constexpr
virtual声明"可被重写"(默认不虚)virtual
override重写基类虚方法(必须写)override(可选)
abstract抽象成员 / 抽象类= 0 纯虚
sealed禁止继续重写 / 继承final
new① 创建对象 ② 隐藏基类成员① new ② 无此概念
base访问基类成员 / 调用基类构造Base:: / 初始化列表
this当前实例;也用于扩展方法的第一个参数this
ref按引用传递(调用处也要写)T&(C# 更显式、更安全)
out输出参数,函数内必须赋值输出参数 T*
in只读引用参数(省拷贝)const T&
params可变参数数组... / initializer_list
value属性的 set 访问器里的隐式参数无
get / set / init属性访问器无(要手写 GetX / SetX)
operator运算符重载 / 转换运算符operator
implicit / explicit隐式 / 显式类型转换运算符转换构造函数 / operator T()
if / else条件分支同
switch / case / default多路分支 / 模式匹配同(但不允许贯穿)
whencase 的附加条件 / 异常筛选无
for计数循环同
foreach / in遍历可枚举对象range-based for
while / do条件循环同
break / continue跳出 / 继续同
return返回同
goto跳转(极少用)同
yield写迭代器 / 惰性序列无(C++20 生成器很不同)
throw抛异常同(C# 只能抛 Exception 派生类)
try / catch / finally异常处理前两个有;没有 finally
lock互斥区,自动加解锁std::lock_guard
checked / unchecked开启 / 关闭整数溢出检查无
async / await异步方法 / 异步等待无(C++20 协程差别很大)
unsafe允许裸指针与指针运算—(C++ 默认就能用)
fixed固定内存地址防止 GC 搬移无(C++ 没有 GC)
stackalloc在栈上分配数组alloca
typeof拿到类型对象(运行期)typeid
nameof把标识符名字变成字符串无(靠宏或手写)
sizeof取大小(非托管类型)sizeof
is / as类型判断 / 安全转型dynamic_cast
typeof / dynamic静态类型对象 / 动态类型typeid / std::any
true / false / null布尔与空true/false;nullptr
void无返回值void
var类型推断auto
object所有类型的根类型无
string / char / bool内置类型别名(映射到 .NET 类型)std::string / char / bool(char 是 2 字节)
where泛型约束C++20 requires / concept
extern声明外部实现(P/Invoke)extern
volatile禁止编译器优化该字段访问volatile(语义有差异)
required要求对象创建时必须初始化该成员(C# 11)无
📝 还有一类"上下文关键字"

C# 里有一批词只在特定位置才是关键字,所以你依然可以拿它们当变量名: value、get、set、init、 add、remove、where、select、 from、let、orderby、join、 on、equals、into、when。 这是为了保持向后兼容——C++ 里加新关键字常会砸坏老代码,C# 用"上下文关键字"绕开了这个问题。

小测验:你现在能写出多少?

Q1:不看上面,写出一个"值语义 + 自动比较 + 自动打印"的类型展开
C#
public record Money(decimal Amount, string Currency);

    var a = new Money(10m, "CNY");
    var b = new Money(10m, "CNY");
    Console.WriteLine(a == b);              // True(按内容比)
    Console.WriteLine(a);                   // Money { Amount = 10, Currency = CNY }
    var c = a with { Amount = 20m };      // 基于 a 改一个字段

一行 record 就替代了 C++ 里"重载 ==、写 <<、写哈希"的一大段样板。详见第 11 章。

Q2:把 C++ 的"遍历容器并筛选出偶数平方和"改写成 C#展开
C++
int sum = 0;
    for (int x : v)
        if (x % 2 == 0)
            sum += x * x;
C#
int sum = v
        .Where(x => x % 2 == 0)
        .Select(x => x * x)
        .Sum();

C# 的写法是"声明我要什么",不是"告诉它怎么循环"。这套 LINQ 是 C++ 里最缺少的那类能力。

Q3:下面哪几行是编译不过的?为什么?展开
C#
A. int? x = null;
    B. int y = null;
    C. char c = "A";
    D. char c = 'A';
    E. if (new List<int>()) { }
    F. string s = "a"; s[0] = 'b';

B、C、E、F 编译不过。
· B:int 是值类型,不能为 null(要写 int?)。
· C:char 必须用单引号,双引号是 string,不能隐式转。
· E:if 只接受 bool;C++ 里"对象能转 bool"这套在 C# 不存在(可以写 if (list != null) 或 if (list.Count > 0))。
· F:string 不可变,索引器只读。

🔑 走完这一章,接着去哪?

语法面扫完了,接下来去理解"为什么这么设计"——那是这套教程真正的主线:
01 为什么会有 C# → 02 编译与运行模型 → 03 内存管理, 之后按 04 到 12 顺序推进即可。

返回 C# 专栏 返回目录