入门
Hello World
C# 程序从 Main() 开始。using System; 导入 System 命名空间(Console、Math 等)。C# 9+ 支持顶级语句:只有 Console.WriteLine("Hello"); 的文件是有效程序。编译器自动生成类和 Main。
using System;
class Program {
static void Main() {
Console.WriteLine("Hello, World!");
}
}变量与类型
C# 是静态类型的。var 让编译器推断类型(编译时仍然类型安全)。decimal(m 后缀)用于需要精确精度的金融计算。const 是编译时常量;readonly 是在构造函数中设置的运行时常量。
int age = 30;
double pi = 3.14159;
decimal price = 19.99m;
char grade = 'A';
bool isDev = true;
string name = "Alice";
var x = 42; // implicitly typed (int)
const double TAX = 0.08;字符串插值
字符串插值($"...")在花括号中嵌入表达式。冒号后的格式说明符控制输出(F2 表示 2 位小数,yyyy-MM-dd 表示日期)。逐字字符串(@"")按字面值处理反斜杠——适用于文件路径和正则表达式。组合两者:$@"..."。
string name = "Alice";
int age = 30;
Console.WriteLine($"Name: {name}, Age: {age}");
// Formatting
double pi = 3.14159;
Console.WriteLine($"Pi: {pi:F2}"); // Pi: 3.14
Console.WriteLine($"Date: {DateTime.Now:yyyy-MM-dd}");
// Verbatim strings (@"") preserve backslashes
string path = @"C:\Users\name";输入与输出
Console.ReadLine() 读取整行作为字符串。int.Parse 转换但无效输入时抛出异常;int.TryParse 更安全——返回 bool 并使用 out 参数。用户输入始终使用 TryParse 以避免坏数据导致的异常。
Console.Write("Enter name: ");
string name = Console.ReadLine();
Console.Write("Enter age: ");
int age = int.Parse(Console.ReadLine());
Console.WriteLine($"Hi {name}, you are {age}");
// Safe parsing with TryParse
if (int.TryParse(Console.ReadLine(), out int num)) {
Console.WriteLine($"Valid: {num}");
} else {
Console.WriteLine("Invalid number");
}可空类型
int? 是可空值类型(Nullable<int>)。?. 运算符(null 条件)安全访问成员——返回 null 而不是抛出异常。?? 运算符(null 合并)为 null 提供默认值。C# 8+ 可空引用类型在编译时警告潜在 null。
int? maybeAge = null; // nullable int
if (maybeAge.HasValue) {
Console.WriteLine(maybeAge.Value);
}
string? maybeName = null; // nullable reference (C# 8+)
int length = maybeName?.Length ?? 0; // null-conditional + null-coalescing
// ValueOrDefault
int age = maybeAge ?? 0; // 0 if null字符串
字符串方法
C# 字符串是不可变的——方法返回新字符串而不是修改原始字符串。常用方法:Length、ToUpper/ToLower、Substring、IndexOf、Replace、Contains、Split。对于大量字符串操作,使用 StringBuilder 避免创建许多中间字符串。
string s = "Hello, World!";
Console.WriteLine(s.Length); // 13
Console.WriteLine(s.ToUpper()); // HELLO, WORLD!
Console.WriteLine(s.Substring(0, 5)); // Hello
Console.WriteLine(s.IndexOf("World")); // 7
Console.WriteLine(s.Replace("World", "C#"));
Console.WriteLine(s.Contains("Hello")); // True
Console.WriteLine(s.Split(", ")); // ["Hello", "World!"]StringBuilder
StringBuilder 是可变的,在循环中构建字符串很高效。字符串连接(+)每次创建新字符串,因此重复连接是 O(n^2)。StringBuilder 摊销为 O(n)。当有多次连接时使用 StringBuilder。
using System.Text;
var sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
sb.Append("Line ").Append(i).Append("\n");
}
string result = sb.ToString();
// vs string concatenation (creates many temporary strings)
// string result = "";
// for (int i = 0; i < 100; i++) result += "Line " + i;格式化与解析
string.Format 使用 {0}、{1} 占位符。格式说明符:F(固定)、N(带分隔符的数字)、P(百分比)、X(十六进制)。解析是区分区域设置的——小数分隔符因区域而异。使用 CultureInfo 进行显式控制,或 TryParse 确保安全。
// String formatting
string s1 = string.Format("{0} is {1} years old", "Alice", 30);
string s2 = $"{3.14159:F2}"; // "3.14"
string s3 = $"{1234567:N0}"; // "1,234,567" (thousands separator)
// Parsing
int n = int.Parse("42");
double d = double.Parse("3.14");
bool b = bool.Parse("true");
// Culture-aware parsing
double euro = double.Parse("3,14",
System.Globalization.CultureInfo.GetCultureInfo("de-DE"));字符串比较
使用 StringComparison.OrdinalIgnoreCase 进行不区分大小写的比较——比 ToLower() 然后 == 更清晰更快。避免对区域敏感的比较使用 ==;使用带 CultureInfo 的 string.Compare。对于字典键,使用 StringComparer.OrdinalIgnoreCase。
string a = "Hello", b = "hello";
// Case-sensitive
bool equal = a == b; // false
bool equal2 = a.Equals(b); // false
// Case-insensitive
bool equalCI = a.Equals(b, StringComparison.OrdinalIgnoreCase); // true
int cmp = string.Compare(a, b, StringComparison.OrdinalIgnoreCase);
// Best practice for comparison
bool same = string.Equals(a, b,
StringComparison.OrdinalIgnoreCase);正则表达式
Regex(来自 System.Text.RegularExpressions)提供模式匹配。使用 @"" 逐字字符串,这样反斜杠不需要双重转义。对于重复使用,编译一次 Regex 实例并重用(RegexOptions.Compiled)以获得更好性能。
using System.Text.RegularExpressions;
string text = "Phone: 123-456-7890";
var match = Regex.Match(text, @"\d{3}-\d{3}-\d{4}");
if (match.Success) Console.WriteLine(match.Value); // 123-456-7890
// Replace
string clean = Regex.Replace(text, @"\d", "X");
// "Phone: XXX-XXX-XXXX"
// Extract all matches
var emails = Regex.Matches("[email protected], [email protected]", @"\S+@\S+");数字与数学
数值类型
C# 有固定大小类型:int(32 位)、long(64 位)、double(64 位浮点)、decimal(128 位,用于金融)。下划线(9_000_000)提高可读性。货币使用 decimal——它避免浮点舍入错误。后缀:L(long)、f(float)、m(decimal)。
int i = 42;
long big = 9_000_000_000L; // underscores for readability
double d = 3.14159265;
float f = 3.14f;
decimal price = 19.99m; // exact decimal for money
byte b = 255;
Console.WriteLine(sizeof(int)); // 4
Console.WriteLine(int.MaxValue); // 2147483647Math 类
Math 为常见操作提供静态方法。Math.Round 默认使用银行家舍入(舍入到偶数)——使用 MidpointRounding.AwayFromZero 进行学校舍入。Math.BigMul 处理 long 乘法以避免溢出。
double x = 2.5;
Math.Pow(x, 3); // 15.625
Math.Sqrt(x); // 1.581
Math.Abs(-5); // 5
Math.Floor(3.7); // 3
Math.Ceiling(3.2); // 4
Math.Round(3.5); // 4 (banker's rounding)
Math.Max(3, 7); // 7
Math.Min(3, 7); // 3
Math.PI; // 3.14159...随机数
Random 生成伪随机数。创建一个实例并重用它(在循环中创建许多实例可能由于基于时间的种子产生重复)。对于加密随机性,使用 System.Security.Cryptography.RandomNumberGenerator。C# 8+ 提供 Random.Shared 用于线程安全。
var random = new Random();
int r = random.Next(1, 101); // 1-100
double d = random.NextDouble(); // 0.0-1.0
byte[] bytes = new byte[4];
random.NextBytes(bytes);
// C# 8+: thread-safe
// var rng = Random.Shared;
// int n = rng.Next(1, 101);
// Array of random items
int[] nums = Enumerable.Range(0, 5)
.Select(_ => random.Next(1, 100)).ToArray();类型转换
当不丢失数据时自动发生隐式转换(int 到 double)。可能丢失精度时需要显式转换(type)。Convert.ToInt32 处理许多类型并舍入(不像转换会截断)。字符串到数字转换始终优先使用 TryParse 以避免异常。
// Implicit (safe, no data loss)
int i = 42;
double d = i; // int -> double
// Explicit cast (may lose data)
double pi = 3.14;
int truncated = (int)pi; // 3
// Convert class
string s = "42";
int n = Convert.ToInt32(s);
string str = Convert.ToString(42);
// Parse / TryParse
int parsed = int.Parse("100");
bool ok = int.TryParse("abc", out int result); // false整数溢出与 checked
默认情况下,整数溢出静默回绕(unchecked)。checked 块在溢出时抛出 OverflowException——用于安全关键代码。对于真正大的数字,BigInteger(System.Numerics)处理任意精度整数而不会溢出。
int a = int.MaxValue; // 2147483647
// a++; // overflow: wraps to -2147483648 (unchecked by default)
checked {
a++; // throws OverflowException!
}
// BigInteger for arbitrary precision
using System.Numerics;
var big = BigInteger.Pow(2, 100);
Console.WriteLine(big); // 1267650...376 (31 digits)控制流
If / Else
if/else if/else 是标准的。三元运算符(condition ? a : b)是简洁的 if/else 表达式。C# 要求布尔条件——不像 C,整数不会隐式转换为 bool。即使单条语句也使用花括号以防止维护 bug。
int score = 85;
if (score >= 90) {
Console.WriteLine("A");
} else if (score >= 80) {
Console.WriteLine("B");
} else {
Console.WriteLine("C");
}
// Ternary operator
string grade = score >= 60 ? "Pass" : "Fail";Switch 与模式匹配
C# 8+ switch 表达式(=>)简洁且返回值。_ 模式是默认情况。模式匹配(is、switch)支持类型模式、属性模式和关系模式(> 18)。这比传统 switch 更强大且减少样板代码。
// Classic switch
int day = 3;
string name = day switch {
1 => "Mon", 2 => "Tue", 3 => "Wed",
6 or 7 => "Weekend",
_ => "Invalid" // default
};
// Pattern matching with types
object obj = "hello";
if (obj is string s && s.Length > 3) {
Console.WriteLine(s); // "hello"
}
// Property patterns
if (person is { Age: > 18, Name: "Alice" }) {
// matches if Age > 18 and Name == "Alice"
}循环
for 用于计数迭代;foreach 迭代集合(数组、列表、IEnumerable);while 循环直到条件为 false。foreach 是只读的——迭代期间不能修改集合。如果需要索引或需要修改元素,使用 for。
// for loop
for (int i = 0; i < 5; i++) {
Console.WriteLine(i);
}
// foreach
string[] fruits = { "apple", "banana", "cherry" };
foreach (string fruit in fruits) {
Console.WriteLine(fruit);
}
// while
int n = 5;
while (n > 0) Console.WriteLine(n--);Break、Continue 与 goto
break 退出最近的循环或 switch;continue 跳到下一次迭代。C# 没有用于嵌套循环的带标签 break——使用标志、提取为带 return 的方法,或使用 LINQ。goto 在 switch 中用于 fall-through 是有效的,但其他方面不鼓励使用。
for (int i = 0; i < 10; i++) {
if (i == 3) continue; // skip 3
if (i == 7) break; // stop at 7
Console.WriteLine(i); // 0 1 2 4 5 6
}
// goto in switch (rare but valid)
switch (code) {
case 1:
DoSomething();
goto case 2; // fall through
case 2:
DoMore();
break;
}迭代器与 yield
yield return 将方法转换为惰性产生值的迭代器——一次一个按需产生。这对大型或无限序列内存高效。编译器生成状态机。yield break 提前结束迭代。LINQ 广泛使用迭代器。
// yield return creates an iterator (lazy evaluation)
IEnumerable<int> GetNumbers() {
for (int i = 0; i < 5; i++) {
yield return i;
}
}
foreach (int n in GetNumbers()) {
Console.WriteLine(n); // 0 1 2 3 4
}
// Infinite sequence (consumed lazily)
IEnumerable<int> Naturals() {
int n = 0;
while (true) yield return n++;
}方法与委托
方法定义
方法可以是表达式体(=>)用于单行。默认参数使参数可选。命名参数(name:)提高多参数调用的可读性并允许跳过可选参数。C# 不支持仅返回类型不同的方法重载。
static int Add(int a, int b) {
return a + b;
}
// Expression-bodied (C# 6+)
static int Multiply(int a, int b) => a * b;
// Default parameters
static void Greet(string name = "Guest", int times = 1) {
for (int i = 0; i < times; i++)
Console.WriteLine($"Hi {name}!");
}
Greet(); // Hi Guest!
Greet("Alice", 3); // Hi Alice! x3
Greet(times: 2); // named argumentout、ref 与 params
out 参数必须由方法赋值(调用者不需要初始化)。ref 参数必须由调用者初始化且可以被修改。params 允许可变数量的参数。C# 7+ 允许内联 out var 声明:TryParse(s, out var result)。
// out: must be assigned in method (caller doesn't init)
bool TryParse(string s, out int result) {
result = 0;
return int.TryParse(s, out result);
}
// ref: caller must initialize
void Increment(ref int n) { n++; }
int x = 5;
Increment(ref x); // x is now 6
// params: variable arguments
int Sum(params int[] nums) => nums.Sum();
Sum(1, 2, 3); // 6
Sum(); // 0委托与事件
委托是类型安全的函数指针。Func<T,TResult> 接受输入并返回值;Action<T> 返回 void;Predicate<T> 返回 bool。事件是带 +=/-= 订阅和 ?.Invoke 安全引发(null 检查)的委托。事件用于观察者模式(发布/订阅)。
// Delegate type
delegate int MathOp(int a, int b);
MathOp add = (a, b) => a + b;
MathOp mul = (a, b) => a * b;
Console.WriteLine(add(3, 4)); // 7
// Built-in delegate types
Func<int, int, int> divide = (a, b) => a / b;
Action<string> log = msg => Console.WriteLine(msg);
Predicate<int> isEven = n => n % 2 == 0;
// Events
event Action<string> OnMessage;
OnMessage += msg => Console.WriteLine($"Got: {msg}");
OnMessage?.Invoke("Hello");Lambda 表达式
Lambda 是使用 => 的匿名方法。它们从封闭作用域捕获变量(闭包)。捕获的变量在 lambda 执行时求值,而不是创建时——在循环中注意这一点。在 LINQ、事件和回调中广泛使用 lambda。
// Lambda with explicit types
Func<int, int> square = (int x) => x * x;
// Type inference
Func<int, int, int> add = (a, b) => a + b;
// Statement lambda
Action<string> log = msg => {
Console.WriteLine($"[{DateTime.Now}] {msg}");
};
// Capturing variables (closures)
int factor = 3;
Func<int, int> multiply = x => x * factor;
Console.WriteLine(multiply(5)); // 15扩展方法
扩展方法在不修改现有类型的情况下向其添加方法。第一个参数前的 this 关键字将其标记为扩展方法。它们必须在静态类中。LINQ 完全实现为 IEnumerable 上的扩展方法。这是添加实用方法的强大方式。
static class StringExtensions {
public static int WordCount(this string s) {
return s.Split(new[] { ' ' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
string text = "Hello World C#";
Console.WriteLine(text.WordCount()); // 3
// LINQ is built on extension methods
var evens = nums.Where(n => n % 2 == 0);集合
List<T>
List<T> 是动态数组(类似 C++ 的 vector 或 Java 的 ArrayList)。Add/Remove 是摊销 O(1) / O(n)。Count 给出元素数量(不是容量)。Find/FindAll 使用谓词。对于频繁的中间插入/删除,LinkedList<T> 可能更好。
using System.Collections.Generic;
var nums = new List<int> { 1, 2, 3 };
nums.Add(4);
nums.AddRange(new[] { 5, 6 });
nums.Remove(3);
nums.RemoveAt(0);
Console.WriteLine(nums.Count); // 4
Console.WriteLine(nums.Contains(4)); // true
// Find
int first = nums.Find(n => n > 3);
List<int> all = nums.FindAll(n => n > 3);Dictionary<TKey, TValue>
Dictionary 是哈希表,平均 O(1) 查找。TryGetValue 比索引器更安全(后者抛出 KeyNotFoundException)。对于不区分大小写的字符串键,向构造函数传递 StringComparer.OrdinalIgnoreCase。Dictionary 不保留插入顺序(需要时使用 OrderedDictionary)。
var ages = new Dictionary<string, int> {
["Alice"] = 30,
["Bob"] = 25
};
ages["Charlie"] = 35;
// Safe access
if (ages.TryGetValue("Alice", out int age)) {
Console.WriteLine($"Alice: {age}");
}
// Iterate
foreach (var kv in ages) {
Console.WriteLine($"{kv.Key}: {kv.Value}");
}
// Case-insensitive keys
var dict = new Dictionary<string, int>(
StringComparer.OrdinalIgnoreCase);HashSet<T> 与 SortedSet<T>
HashSet<T> 存储唯一元素,O(1) 查找——用于去重和成员测试。它支持集合操作(Union、Intersect、Except)。SortedSet<T>(红黑树)保持元素有序,O(log n) 操作。顺序不重要时使用 HashSet,重要时使用 SortedSet。
var set = new HashSet<int> { 1, 2, 3 };
set.Add(2); // duplicate, ignored
set.Add(4);
Console.WriteLine(set.Count); // 4
Console.WriteLine(set.Contains(3)); // true
// Set operations
var other = new HashSet<int> { 3, 4, 5 };
set.IntersectWith(other); // set: {3, 4}
set.UnionWith(other); // set: {3, 4, 5}
// SortedSet keeps elements sorted
var sorted = new SortedSet<int> { 3, 1, 2 }; // {1, 2, 3}Queue<T> 与 Stack<T>
Queue<T> 是 FIFO(Enqueue/Dequeue)——用于任务调度、BFS。Stack<T> 是 LIFO(Push/Pop)——用于撤销/重做、DFS、表达式求值。两者的核心操作都是 O(1)。对于并发场景,使用 System.Collections.Concurrent 的 ConcurrentQueue 和 ConcurrentStack。
// Queue: FIFO (first in, first out)
var queue = new Queue<string>();
queue.Enqueue("first");
queue.Enqueue("second");
string next = queue.Dequeue(); // "first"
string peek = queue.Peek(); // "second"
// Stack: LIFO (last in, first out)
var stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
int top = stack.Pop(); // 2
int peek = stack.Peek(); // 1数组
数组是固定大小的。Array.Sort 就地排序。多维数组([,])是矩形的(统一列)。交错数组([][])是数组的数组(行可以有不同长度)。动态调整大小使用 List<T>;固定大小、性能关键的数据使用数组。
int[] nums = { 1, 2, 3, 4, 5 };
Console.WriteLine(nums.Length); // 5
Array.Sort(nums);
Array.Reverse(nums);
int found = Array.IndexOf(nums, 3);
int[] copy = new int[5];
Array.Copy(nums, copy, 5);
// Multidimensional
int[,] grid = { { 1, 2 }, { 3, 4 } };
Console.WriteLine(grid[1, 0]); // 3
// Jagged array (array of arrays)
int[][] jagged = { new[] { 1, 2 }, new[] { 3, 4, 5 } };类与面 向对象
类与属性
自动属性({ get; set; })自动生成支持字段。表达式体属性(=>)在访问时计算。C# 9+ 仅 init 属性({ get; init; })仅在构造期间可设置,实现不可变性。对象初始化器:new Person { Name = "X" }。
class Person {
// Auto-properties (C# 3+)
public string Name { get; set; }
public int Age { get; set; }
// Read-only property
public bool IsAdult => Age >= 18;
// Constructor
public Person(string name, int age) {
Name = name;
Age = age;
}
}
var p = new Person("Alice", 30);
Console.WriteLine(p.IsAdult); // true继承与 virtual
virtual 标记方法可重写;子类中的 override 提供新实现。C# 要求显式 override(不像 Java)。sealed 防止类被继承或方法被重写。所有类隐式继承自 object(System.Object)。
class Animal {
public virtual void Speak() {
Console.WriteLine("...");
}
}
class Dog : Animal {
public override void Speak() {
Console.WriteLine("Woof");
}
}
Animal a = new Dog();
a.Speak(); // Woof (polymorphism)
// sealed prevents further inheritance
sealed class Puppy : Dog { }接口
接口定义没有实现的契约。一个类可以实现多个接口(不像单类继承)。C# 8+ 允许默认接口方法。接口用于多态和依赖注入。命名约定:前缀 I(IShape、ILogger)。
interface IShape {
double Area(); // interface member
double Perimeter { get; }
}
class Circle : IShape {
public double Radius { get; set; }
public double Area() => Math.PI * Radius * Radius;
public double Perimeter => 2 * Math.PI * Radius;
}
// Default interface methods (C# 8+)
interface ILogger {
void Log(string msg);
void Error(string msg) => Log($"ERROR: {msg}"); // default
}抽象类
抽象类不能被实例化,可以同时有抽象(必须重写)和具体(virtual)成员。有共享实现时使用抽象类;纯契约使用接口。一个类只能继承一个抽象类但可以实现多个接口。
abstract class Shape {
public abstract double Area(); // must be overridden
public virtual void Describe() {
Console.WriteLine($"Area: {Area()}");
}
}
class Rectangle : Shape {
public double Width { get; set; }
public double Height { get; set; }
public override double Area() => Width * Height;
}
// Cannot instantiate abstract class
// var s = new Shape(); // ERROR
var r = new Rectangle { Width = 3, Height = 4 };Record 与 Struct
Record(C# 9+)提供基于值的相等性、不可变性和简洁语法——非常适合 DTO 和数据模型。with 表达式创建修改属性的副本。Struct 是值类型(赋值时拷贝,栈分配)——用于小型轻量数据。类是引用类型(堆分配)。
// Record (C# 9+): value-based equality, immutability
record Point(int X, int Y);
var p1 = new Point(3, 4);
var p2 = new Point(3, 4);
Console.WriteLine(p1 == p2); // true (value equality)
// with expression (non-destructive mutation)
var p3 = p1 with { X = 10 };
// Struct: value type (stack-allocated)
struct Vector {
public double X, Y;
public Vector(double x, double y) { X = x; Y = y; }
}LINQ
Where 与 Select
LINQ(语言集成查询)对集合提供类 SQL 查询。Where 过滤,Select 转换(映射)。LINQ 是惰性的——查询仅在枚举时执行(例如通过 ToList())。这使高效链式操作无需中间集合成为可能。方法语法(上)最常见;查询语法也可用。
using System.Linq;
int[] nums = { 1, 2, 3, 4, 5, 6 };
// Filter
var evens = nums.Where(n => n % 2 == 0); // 2, 4, 6
// Map/Transform
var squares = nums.Select(n => n * n); // 1, 4, 9, 16, 25, 36
// Chain operations
var result = nums
.Where(n => n > 2)
.Select(n => n * 10)
.ToList(); // 30, 40, 50, 60排序与分组
OrderBy/OrderByDescending 排序;ThenBy 添加次要排序条件。GroupBy 按键聚集元素,返回 IGrouping<key, element> 组。每个组有一个 Key 且本身是其成员的 IEnumerable。这取代了分组的手动循环和字典。
var people = new[] {
new { Name = "Alice", Age = 30 },
new { Name = "Bob", Age = 25 },
new { Name = "Charlie", Age = 30 },
};
// Order
var sorted = people.OrderBy(p => p.Age).ThenBy(p => p.Name);
var desc = people.OrderByDescending(p => p.Age);
// Group
var grouped = people.GroupBy(p => p.Age);
foreach (var g in grouped) {
Console.WriteLine($"Age {g.Key}: {g.Count()} people");
}
// Age 25: 1 people
// Age 30: 2 people聚合
LINQ 聚合方法(Sum、Average、Min、Max、Count)从集合计算单个值。Aggregate 最通用——它是一个 fold/reduce,累积应用函数。这些在空序列上抛出异常;使用 *OrDefault 变体或先检查是否为空。
int[] nums = { 1, 2, 3, 4, 5 };
Console.WriteLine(nums.Sum()); // 15
Console.WriteLine(nums.Average()); // 3
Console.WriteLine(nums.Min()); // 1
Console.WriteLine(nums.Max()); // 5
Console.WriteLine(nums.Count()); // 5
// Count with predicate
int evens = nums.Count(n => n % 2 == 0); // 2
// Aggregate (fold/reduce)
int product = nums.Aggregate((a, b) => a * b); // 120
string joined = nums.Aggregate("", (s, n) => s + n); // "12345"First、Single 与 ElementAt
First 返回第一个元素(空时抛出异常);FirstOrDefault 未找到时返回默认值(int 为 0,引用类型为 null)。Single 要求恰好一个元素(否则抛出异常)——用于验证。Any/All 返回 bool 而不枚举整个集合(短路)。
int[] nums = { 1, 2, 3, 4, 5 };
int first = nums.First(); // 1
int firstEven = nums.First(n => n % 2 == 0); // 2
int firstOrDef = nums.FirstOrDefault(n => n > 10); // 0 (default)
int single = new[] { 42 }.Single(); // 42
// nums.Single(); // throws! more than one element
int third = nums.ElementAt(2); // 3 (0-indexed)
bool any = nums.Any(n => n > 3); // true
bool all = nums.All(n => n > 0); // trueJoin 与 Zip
Join 执行内连接(类似 SQL)按键匹配元素。Zip 按位置配对两个序列的元素。两者都产生新序列。LINQ 还支持 GroupJoin(带分组的左连接)。这些对于组合来自多个源的相关数据很强大。
var users = new[] {
new { Id = 1, Name = "Alice" },
new { Id = 2, Name = "Bob" },
};
var orders = new[] {
new { UserId = 1, Item = "Book" },
new { Id = 2, Item = "Pen" },
};
// Join (inner join)
var joined = users.Join(orders,
u => u.Id, o => o.UserId,
(u, o) => new { u.Name, o.Item });
// { Alice, Book }
// Zip (pair elements by position)
var names = new[] { "Alice", "Bob" };
var ages = new[] { 30, 25 };
var pairs = names.Zip(ages, (n, a) => $"{n}: {a}");
// "Alice: 30", "Bob: 25"错误处理
Try / Catch / Finally
先捕获特定异常,最后捕获一般 Exception(最具体到最不具体)。finally 始终运行——用于清理(关闭文件、释放资源)。避免 catch (Exception) 用于控制流;只捕获你能处理的。使用 throw;(不是 throw e;)保留栈跟踪。
try {
int x = 10;
int y = 0;
int z = x / y; // throws DivideByZeroException
} catch (DivideByZeroException e) {
Console.WriteLine($"Error: {e.Message}");
} catch (Exception e) {
Console.WriteLine($"Unexpected: {e.Message}");
} finally {
// always runs, even with return/throw
Console.WriteLine("Cleanup");
}