C# 核心知识精讲:多线程、网络编程(UDP/TCP)与 LINQ 方法全解析
C# 核心知识精讲:多线程、网络编程(UDP/TCP)与 LINQ 方法全解析
本文系统梳理 C# 开发中四大核心模块:多线程编程、UDP 网络通信、TCP 网络通信以及 LINQ 数据查询方法。每个模块均配有概念讲解与可运行代码示例,适合学习复习与面试备考。
前言
在 C# 开发中,多线程、网络编程和 LINQ 是三个高频使用且相互关联的核心技术领域:
- 多线程是提升程序并发性能、避免 UI 阻塞的基础;
- **网络编程(UDP/TCP)**是实现分布式通信、客户端-服务器架构的关键;
- LINQ 则极大简化了数据查询与集合操作,是 C# 开发者必备技能。
本文将从基础概念出发,结合实际代码示例,逐一深入讲解这三大模块。
一、C# 多线程编程
1.1 多线程基础概念
进程(Process):操作系统资源分配的基本单位,一个应用程序运行时就是一个进程。
线程(Thread):CPU 调度的最小单位,一个进程可以包含多个线程,它们共享进程的内存资源。
多线程的优势:
- 提高程序响应速度(UI 线程不被阻塞)
- 充分利用多核 CPU 资源
- 并行处理耗时任务(IO、计算等)
1.2 Thread 类
System.Threading.Thread 是最基础的线程类,用于创建和控制线程。
using System;
using System.Threading;
class ThreadDemo
{
static void Main()
{
// 创建线程
Thread t = new Thread(Worker);
t.Name = "工作线程1";
t.IsBackground = true; // 设置为后台线程
t.Start(); // 启动线程
// 主线程继续执行
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"主线程: {i}");
Thread.Sleep(100);
}
t.Join(); // 等待工作线程完成
Console.WriteLine("程序结束");
}
static void Worker()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"{Thread.CurrentThread.Name}: {i}");
Thread.Sleep(150);
}
}
}
常用属性与方法:
| 成员 | 说明 |
|---|---|
Name |
获取或设置线程名称 |
IsBackground |
获取或设置是否为后台线程 |
IsAlive |
获取线程是否处于活动状态 |
Start() |
启动线程 |
Sleep(int) |
静态方法,暂停当前线程指定毫秒 |
Join() |
等待线程终止 |
Abort() |
终止线程(已不推荐使用) |
1.3 线程池 ThreadPool
线程池维护一组工作线程,避免频繁创建和销毁线程带来的开销。
using System;
using System.Threading;
class ThreadPoolDemo
{
static void Main()
{
// 将工作项排入线程池
for (int i = 0; i < 5; i++)
{
ThreadPool.QueueUserWorkItem(Worker, i);
}
Console.WriteLine("主线程继续运行...");
Thread.Sleep(2000); // 等待线程池任务完成
Console.WriteLine("所有任务完成");
}
static void Worker(object state)
{
int taskId = (int)state;
Console.WriteLine($"任务 {taskId} 在线程 {Thread.CurrentThread.ManagedThreadId} 上执行");
Thread.Sleep(500);
}
}
线程池特点:
- 线程复用,减少创建销毁开销
- 自动管理线程数量
- 适合短时间运行的任务
- 不适合长时间运行的任务(会占用池资源)
1.4 Task 与 async/await
Task 和 Task<T> 是 .NET 推荐的异步编程模型,配合 async/await 关键字使用。
using System;
using System.Threading.Tasks;
class TaskDemo
{
static async Task Main()
{
Console.WriteLine("开始异步操作...");
// 方式一:Task.Run
Task<string> task1 = Task.Run(() =>
{
Task.Delay(1000).Wait();
return "Task.Run 结果";
});
// 方式二:async 方法
string result2 = await DoWorkAsync();
string result1 = await task1;
Console.WriteLine(result1);
Console.WriteLine(result2);
// 并行等待多个任务
Task[] tasks = new Task[]
{
Task.Delay(500),
Task.Delay(800),
Task.Delay(300)
};
await Task.WhenAll(tasks);
Console.WriteLine("所有并行任务完成");
}
static async Task<string> DoWorkAsync()
{
await Task.Delay(1500);
return "async 方法结果";
}
}
Task 常用方法:
| 方法 | 说明 |
|---|---|
Task.Run() |
将工作排入线程池并返回 Task |
Task.Delay() |
异步延迟 |
Task.WhenAll() |
等待所有任务完成 |
Task.WhenAny() |
等待任一任务完成 |
ContinueWith() |
任务完成后继续执行 |
Result |
同步获取结果(可能死锁,慎用) |
1.5 线程安全与锁
多线程环境下,共享资源的访问需要同步控制,否则会出现数据竞争。
using System;
using System.Threading;
using System.Threading.Tasks;
class LockDemo
{
private static int _counter = 0;
private static readonly object _lockObj = new object();
static async Task Main()
{
// 不加锁的情况
Task[] tasks = new Task[10];
for (int i = 0; i < 10; i++)
{
tasks[i] = Task.Run(() =>
{
for (int j = 0; j < 1000; j++)
{
lock (_lockObj) // 加锁保证线程安全
{
_counter++;
}
}
});
}
await Task.WhenAll(tasks);
Console.WriteLine($"最终计数: {_counter}"); // 预期 10000
}
}
常用同步机制:
| 机制 | 说明 |
|---|---|
lock |
最常用的互斥锁,基于 Monitor |
Monitor |
提供 Enter/Exit/Pulse/Wait 等方法 |
Mutex |
跨进程互斥锁 |
Semaphore |
信号量,限制并发数量 |
ReaderWriterLockSlim |
读写分离锁,多读单写 |
Interlocked |
原子操作(递增、交换等) |
ConcurrentQueue<T> 等 |
线程安全集合 |
二、C# 网络编程 — UDP
2.1 UDP 简介
UDP(User Datagram Protocol,用户数据报协议) 是一种无连接的传输层协议:
- 无连接:发送数据前不需要建立连接
- 不可靠:不保证数据送达、不保证顺序、不重传
- 开销小:头部只有 8 字节,传输效率高
- 支持广播和多播
适用场景:视频直播、DNS 查询、游戏实时通信、物联网传感器数据等对实时性要求高、可容忍少量丢包的场景。
2.2 UdpClient 类
System.Net.Sockets.UdpClient 是 .NET 中用于 UDP 通信的封装类。
常用方法:
| 方法 | 说明 |
|---|---|
UdpClient(int port) |
绑定到指定端口 |
Send(byte[], int, IPEndPoint) |
发送数据报 |
Receive(ref IPEndPoint) |
接收数据报(阻塞) |
ReceiveAsync() |
异步接收数据报 |
Close() |
关闭连接 |
2.3 UDP 服务端示例
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class UdpServer
{
static async Task Main(string[] args)
{
int port = 8888;
UdpClient udpServer = new UdpClient(port);
Console.WriteLine($"UDP 服务端已启动,监听端口 {port}...");
try
{
while (true)
{
// 异步接收数据
UdpReceiveResult result = await udpServer.ReceiveAsync();
string message = Encoding.UTF8.GetString(result.Buffer);
IPEndPoint remoteEndPoint = result.RemoteEndPoint;
Console.WriteLine($"收到来自 {remoteEndPoint} 的消息: {message}");
// 回复客户端
string reply = $"服务端已收到: {message}";
byte[] replyBytes = Encoding.UTF8.GetBytes(reply);
await udpServer.SendAsync(replyBytes, replyBytes.Length, remoteEndPoint);
}
}
catch (Exception ex)
{
Console.WriteLine($"服务端异常: {ex.Message}");
}
finally
{
udpServer.Close();
}
}
}
2.4 UDP 客户端示例
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class UdpClientDemo
{
static async Task Main(string[] args)
{
string serverIp = "127.0.0.1";
int serverPort = 8888;
UdpClient client = new UdpClient();
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(serverIp), serverPort);
try
{
// 发送消息
string message = "Hello, UDP Server!";
byte[] data = Encoding.UTF8.GetBytes(message);
await client.SendAsync(data, data.Length, serverEndPoint);
Console.WriteLine($"已发送: {message}");
// 接收回复(设置超时)
client.Client.ReceiveTimeout = 3000;
UdpReceiveResult result = await client.ReceiveAsync();
string reply = Encoding.UTF8.GetString(result.Buffer);
Console.WriteLine($"收到回复: {reply}");
}
catch (SocketException ex)
{
Console.WriteLine($"网络异常: {ex.Message}");
}
finally
{
client.Close();
}
}
}
2.5 UDP 广播示例
UDP 支持向同一网段内所有主机发送广播消息。
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class UdpBroadcast
{
static async Task Main(string[] args)
{
int port = 9000;
UdpClient broadcaster = new UdpClient();
broadcaster.EnableBroadcast = true; // 启用广播
IPEndPoint broadcastEndPoint = new IPEndPoint(IPAddress.Broadcast, port);
for (int i = 0; i < 5; i++)
{
string msg = $"广播消息 #{i + 1} - {DateTime.Now:HH:mm:ss}";
byte[] data = Encoding.UTF8.GetBytes(msg);
await broadcaster.SendAsync(data, data.Length, broadcastEndPoint);
Console.WriteLine($"已广播: {msg}");
await Task.Delay(1000);
}
broadcaster.Close();
Console.WriteLine("广播结束");
}
}
三、C# 网络编程 — TCP
3.1 TCP 简介
TCP(Transmission Control Protocol,传输控制协议) 是一种面向连接的、可靠的传输层协议:
- 面向连接:通信前需通过三次握手建立连接
- 可靠传输:保证数据有序、无差错、不丢失、不重复
- 流量控制与拥塞控制:自动调整发送速率
- 全双工:连接建立后双方可同时收发数据
适用场景:文件传输(FTP/HTTP)、邮件(SMTP)、远程登录(SSH)、数据库连接等要求数据完整性的场景。
3.2 TcpListener 与 TcpClient
| 类 | 用途 |
|---|---|
TcpListener |
TCP 服务端,监听客户端连接请求 |
TcpClient |
TCP 客户端,用于连接服务端并收发数据 |
NetworkStream |
网络数据流,用于读写数据 |
3.3 TCP 服务端示例
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
class TcpServer
{
static async Task Main(string[] args)
{
IPAddress ip = IPAddress.Any;
int port = 8888;
TcpListener listener = new TcpListener(ip, port);
listener.Start();
Console.WriteLine($"TCP 服务端已启动,监听 {ip}:{port}");
try
{
while (true)
{
// 等待客户端连接
TcpClient client = await listener.AcceptTcpClientAsync();
Console.WriteLine($"客户端已连接: {client.Client.RemoteEndPoint}");
// 每个客户端独立线程处理
_ = HandleClientAsync(client);
}
}
catch (Exception ex)
{
Console.WriteLine($"服务端异常: {ex.Message}");
}
finally
{
listener.Stop();
}
}
static async Task HandleClientAsync(TcpClient client)
{
try
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
while (true)
{
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0) // 客户端断开
{
Console.WriteLine("客户端断开连接");
break;
}
string received = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"收到: {received}");
// 回显(Echo)
byte[] reply = Encoding.UTF8.GetBytes($"服务端回显: {received}");
await stream.WriteAsync(reply, 0, reply.Length);
}
}
catch (Exception ex)
{
Console.WriteLine($"处理客户端异常: {ex.Message}");
}
finally
{
client.Close();
}
}
}
3.4 TCP 客户端示例
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
class TcpClientDemo
{
static async Task Main(string[] args)
{
string serverIp = "127.0.0.1";
int port = 8888;
TcpClient client = new TcpClient();
try
{
// 连接服务端
await client.ConnectAsync(serverIp, port);
Console.WriteLine($"已连接到服务端 {serverIp}:{port}");
NetworkStream stream = client.GetStream();
// 发送消息
string message = "Hello, TCP Server!";
byte[] data = Encoding.UTF8.GetBytes(message);
await stream.WriteAsync(data, 0, data.Length);
Console.WriteLine($"已发送: {message}");
// 接收回复
byte[] buffer = new byte[1024];
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
string reply = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"收到: {reply}");
}
catch (SocketException ex)
{
Console.WriteLine($"连接失败: {ex.Message}");
}
finally
{
client.Close();
}
}
}
3.5 TCP 粘包问题与解决方案
TCP 是面向流的协议,数据没有明确的边界,可能出现粘包(多个数据包合并)或拆包(一个数据包被拆分)的情况。
常见解决方案:
- 固定长度:每条消息固定长度
- 特殊分隔符:用特定字符(如
\n、\r\n)分隔 - 长度前缀:消息前加 4 字节表示消息长度(推荐)
// 长度前缀方式发送消息
public static async Task SendMessageAsync(NetworkStream stream, string message)
{
byte[] body = Encoding.UTF8.GetBytes(message);
byte[] lengthPrefix = BitConverter.GetBytes(body.Length);
// 先发送长度(4字节),再发送内容
await stream.WriteAsync(lengthPrefix, 0, 4);
await stream.WriteAsync(body, 0, body.Length);
}
// 长度前缀方式接收消息
public static async Task<string> ReceiveMessageAsync(NetworkStream stream)
{
byte[] lengthBuffer = new byte[4];
int totalRead = 0;
// 读取4字节长度
while (totalRead < 4)
{
int read = await stream.ReadAsync(lengthBuffer, totalRead, 4 - totalRead);
if (read == 0) return null;
totalRead += read;
}
int bodyLength = BitConverter.ToInt32(lengthBuffer, 0);
byte[] bodyBuffer = new byte[bodyLength];
totalRead = 0;
// 读取完整消息体
while (totalRead < bodyLength)
{
int read = await stream.ReadAsync(bodyBuffer, totalRead, bodyLength - totalRead);
if (read == 0) return null;
totalRead += read;
}
return Encoding.UTF8.GetString(bodyBuffer);
}
3.6 UDP 与 TCP 对比
| 特性 | UDP | TCP |
|---|---|---|
| 连接性 | 无连接 | 面向连接 |
| 可靠性 | 不可靠 | 可靠 |
| 顺序性 | 不保证顺序 | 保证顺序 |
| 传输速度 | 快 | 相对较慢 |
| 头部开销 | 8 字节 | 20 字节(不含选项) |
| 流量控制 | 无 | 有 |
| 拥塞控制 | 无 | 有 |
| 广播/多播 | 支持 | 不支持 |
| 适用场景 | 实时音视频、游戏、DNS | 文件传输、Web、邮件 |
四、LINQ 方法详解
4.1 LINQ 简介
LINQ(Language Integrated Query,语言集成查询) 是 C# 3.0 引入的一项特性,允许使用类似 SQL 的语法直接在 C# 代码中查询数据。
LINQ 的两种语法:
- 查询语法(Query Syntax):类似 SQL,使用
from、where、select等关键字 - 方法语法(Method Syntax):使用扩展方法链式调用
// 查询语法
var querySyntax = from s in students
where s.Age > 18
orderby s.Name
select s;
// 方法语法(等价)
var methodSyntax = students
.Where(s => s.Age > 18)
.OrderBy(s => s.Name)
.Select(s => s);
4.2 数据准备
以下示例使用如下测试数据:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Major { get; set; }
public decimal Score { get; set; }
}
List<Student> students = new List<Student>
{
new Student { Id = 1, Name = "张三", Age = 20, Major = "计算机", Score = 85.5m },
new Student { Id = 2, Name = "李四", Age = 22, Major = "数学", Score = 92.0m },
new Student { Id = 3, Name = "王五", Age = 19, Major = "计算机", Score = 78.5m },
new Student { Id = 4, Name = "赵六", Age = 21, Major = "英语", Score = 88.0m },
new Student { Id = 5, Name = "钱七", Age = 20, Major = "数学", Score = 95.5m },
new Student { Id = 6, Name = "孙八", Age = 23, Major = "计算机", Score = 67.0m }
};
4.3 筛选与投影
Where — 条件筛选
// 筛选计算机专业且分数大于80的学生
var result = students.Where(s => s.Major == "计算机" && s.Score > 80);
foreach (var s in result)
Console.WriteLine($"{s.Name} - {s.Major} - {s.Score}");
Select — 投影(转换)
// 只选取姓名和分数,投影为匿名类型
var names = students.Select(s => new { s.Name, s.Score });
foreach (var n in names)
Console.WriteLine($"{n.Name}: {n.Score}");
// Select 带索引重载
var indexed = students.Select((s, index) => $"{index + 1}. {s.Name}");
SelectMany — 扁平化集合
// 将嵌套集合扁平化为单一序列
List<string[]> courses = new List<string[]>
{
new string[] { "C#", "Java" },
new string[] { "Python", "Go", "Rust" }
};
var allCourses = courses.SelectMany(c => c);
// 结果: C#, Java, Python, Go, Rust
4.4 排序
OrderBy / OrderByDescending
// 按分数升序
var ascending = students.OrderBy(s => s.Score);
// 按分数降序
var descending = students.OrderByDescending(s => s.Score);
ThenBy / ThenByDescending — 多级排序
// 先按专业升序,再按分数降序
var multiSort = students
.OrderBy(s => s.Major)
.ThenByDescending(s => s.Score);
Reverse — 反转序列
var reversed = students.Reverse();
4.5 分组
GroupBy — 分组
// 按专业分组
var groups = students.GroupBy(s => s.Major);
foreach (var group in groups)
{
Console.WriteLine($"专业: {group.Key},人数: {group.Count()}");
foreach (var s in group)
Console.WriteLine($" - {s.Name} ({s.Score})");
}
ToLookup — 生成查找表
// ToLookup 生成 ILookup<TKey, TElement>,类似字典但值是集合
var lookup = students.ToLookup(s => s.Major);
foreach (var s in lookup["计算机"])
Console.WriteLine(s.Name);
4.6 集合操作
Distinct — 去重
int[] numbers = { 1, 2, 2, 3, 3, 3, 4 };
var unique = numbers.Distinct(); // 1, 2, 3, 4
Union / Intersect / Except
int[] a = { 1, 2, 3, 4 };
int[] b = { 3, 4, 5, 6 };
var union = a.Union(b); // 并集: 1,2,3,4,5,6
var intersect = a.Intersect(b); // 交集: 3,4
var except = a.Except(b); // 差集(a有b无): 1,2
Concat — 连接序列
var concat = a.Concat(b); // 1,2,3,4,3,4,5,6(不去重)
4.7 聚合操作
| 方法 | 说明 | 示例 |
|---|---|---|
Count() |
元素数量 | students.Count() |
Count(predicate) |
满足条件的数量 | students.Count(s => s.Score > 80) |
Sum() |
求和 | students.Sum(s => s.Score) |
Average() |
平均值 | students.Average(s => s.Score) |
Max() |
最大值 | students.Max(s => s.Score) |
Min() |
最小值 | students.Min(s => s.Score) |
Aggregate() |
自定义聚合 | 见下方示例 |
// Aggregate 自定义聚合:计算所有分数的乘积(示例)
decimal product = students.Aggregate(1m, (acc, s) => acc * s.Score);
// Aggregate 拼接字符串
string allNames = students.Aggregate("", (acc, s) => acc + s.Name + ", ");
// 结果: "张三, 李四, 王五, 赵六, 钱七, 孙八, "
4.8 元素操作
| 方法 | 说明 | 异常行为 |
|---|---|---|
First() |
第一个元素 | 无元素抛异常 |
FirstOrDefault() |
第一个元素或默认值 | 无元素返回默认值 |
Last() |
最后一个元素 | 无元素抛异常 |
LastOrDefault() |
最后一个或默认值 | 无元素返回默认值 |
Single() |
唯一元素 | 0个或多个都抛异常 |
SingleOrDefault() |
唯一元素或默认值 | 多个抛异常 |
ElementAt(int) |
指定索引元素 | 越界抛异常 |
ElementAtOrDefault(int) |
指定索引或默认值 | 越界返回默认值 |
var first = students.First(s => s.Major == "计算机"); // 张三
var firstOrDefault = students.FirstOrDefault(s => s.Major == "物理"); // null
var single = students.Single(s => s.Id == 1); // 张三
// students.Single(s => s.Major == "计算机") // 抛异常(有多个)
4.9 量词操作
| 方法 | 说明 |
|---|---|
Any() |
是否有元素 |
Any(predicate) |
是否有满足条件的元素 |
All(predicate) |
是否所有元素都满足条件 |
Contains(value) |
是否包含指定元素 |
bool hasHighScore = students.Any(s => s.Score > 90); // true
bool allPassed = students.All(s => s.Score >= 60); // true
bool hasZhang = students.Select(s => s.Name).Contains("张三"); // true
4.10 分区操作
| 方法 | 说明 |
|---|---|
Take(n) |
取前 n 个元素 |
Skip(n) |
跳过前 n 个元素 |
TakeWhile(predicate) |
取满足条件的元素,直到不满足 |
SkipWhile(predicate) |
跳过满足条件的元素,直到不满足 |
var top3 = students.OrderByDescending(s => s.Score).Take(3); // 前三名
var page2 = students.OrderBy(s => s.Id).Skip(3).Take(3); // 分页第二页
4.11 连接操作
Join — 内连接
// 准备第二个数据源
var departments = new List<Department>
{
new Department { Major = "计算机", DeptName = "信息工程学院" },
new Department { Major = "数学", DeptName = "理学院" },
new Department { Major = "英语", DeptName = "外国语学院" }
};
// 内连接:学生与学院信息关联
var joinResult = students.Join(
departments,
s => s.Major, // 左表键
d => d.Major, // 右表键
(s, d) => new { s.Name, s.Major, d.DeptName }
);
foreach (var item in joinResult)
Console.WriteLine($"{item.Name} - {item.Major} - {item.DeptName}");
GroupJoin — 分组连接(左外连接)
var groupJoin = departments.GroupJoin(
students,
d => d.Major,
s => s.Major,
(d, studentGroup) => new
{
d.DeptName,
Students = studentGroup.ToList()
}
);
4.12 转换操作
| 方法 | 说明 |
|---|---|
ToList() |
转为 List<T> |
ToArray() |
转为数组 |
ToDictionary() |
转为字典 |
ToHashSet() |
转为 HashSet |
Cast<T>() |
强制转换元素类型 |
OfType<T>() |
筛选指定类型元素 |
AsEnumerable() |
转为 IEnumerable<T> |
AsQueryable() |
转为 IQueryable<T> |
// ToDictionary
var dict = students.ToDictionary(s => s.Id, s => s.Name);
// OfType 筛选类型
object[] mixed = { 1, "hello", 2, "world", 3 };
var strings = mixed.OfType<string>(); // "hello", "world"
var ints = mixed.OfType<int>(); // 1, 2, 3
4.13 生成操作
| 方法 | 说明 |
|---|---|
Enumerable.Range(start, count) |
生成整数序列 |
Enumerable.Repeat(element, count) |
重复元素生成序列 |
Enumerable.Empty<T>() |
空序列 |
var range = Enumerable.Range(1, 10); // 1,2,...,10
var repeat = Enumerable.Repeat("A", 5); // A,A,A,A,A
var empty = Enumerable.Empty<int>(); // 空序列
4.14 延迟执行 vs 立即执行
延迟执行(Deferred Execution):查询在迭代时才执行,每次迭代重新计算。
var query = students.Where(s => s.Score > 80); // 此时未执行
students.Add(new Student { Id = 7, Name = "周九", Age = 20, Major = "计算机", Score = 90 });
foreach (var s in query) // 迭代时执行,包含新添加的周九
Console.WriteLine(s.Name);
立即执行(Immediate Execution):调用时立即执行并缓存结果。
var list = students.Where(s => s.Score > 80).ToList(); // 立即执行
students.Add(new Student { Id = 8, Name = "吴十", Age = 20, Major = "计算机", Score = 91 });
// list 中不包含吴十,因为已经执行完毕
判断规则:
- 返回
IEnumerable<T>/IQueryable<T>→ 通常延迟执行 - 返回单个值(
Count、Sum、First、Any等)或集合(ToList、ToArray、ToDictionary)→ 立即执行
4.15 LINQ 综合实战示例
// 需求:统计每个专业的平均分、最高分、人数,按平均分降序排列
var report = students
.GroupBy(s => s.Major)
.Select(g => new
{
Major = g.Key,
Count = g.Count(),
AvgScore = g.Average(s => s.Score),
MaxScore = g.Max(s => s.Score),
TopStudent = g.OrderByDescending(s => s.Score).First().Name
})
.OrderByDescending(r => r.AvgScore)
.ToList();
Console.WriteLine("| 专业 | 人数 | 平均分 | 最高分 | 最高分学生 |");
Console.WriteLine("|------|------|--------|--------|------------|");
foreach (var r in report)
Console.WriteLine($"| {r.Major} | {r.Count} | {r.AvgScore:F1} | {r.MaxScore} | {r.TopStudent} |");
五、多线程 + 网络编程 + LINQ 综合应用
下面是一个综合示例:使用多线程并发处理多个 TCP 客户端连接,并用 LINQ 统计连接信息。
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
class IntegratedServer
{
// 线程安全的连接信息集合
private static ConcurrentDictionary<string, ClientInfo> _clients = new();
static async Task Main(string[] args)
{
TcpListener listener = new TcpListener(IPAddress.Any, 8888);
listener.Start();
Console.WriteLine("综合服务端已启动...");
// 后台定时统计
_ = Task.Run(async () =>
{
while (true)
{
await Task.Delay(5000);
PrintStats();
}
});
while (true)
{
TcpClient client = await listener.AcceptTcpClientAsync();
string clientId = client.Client.RemoteEndPoint!.ToString()!;
_clients[clientId] = new ClientInfo { Id = clientId, ConnectTime = DateTime.Now };
Console.WriteLine($"客户端连接: {clientId}");
_ = HandleClientAsync(client, clientId);
}
}
static async Task HandleClientAsync(TcpClient client, string clientId)
{
try
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
int totalBytes = 0;
while (true)
{
int read = await stream.ReadAsync(buffer, 0, buffer.Length);
if (read == 0) break;
totalBytes += read;
string msg = Encoding.UTF8.GetString(buffer, 0, read);
Console.WriteLine($"[{clientId}] {msg}");
byte[] reply = Encoding.UTF8.GetBytes($"已收到 {read} 字节");
await stream.WriteAsync(reply, 0, reply.Length);
}
if (_clients.TryGetValue(clientId, out var info))
info.TotalBytes = totalBytes;
}
catch (Exception ex)
{
Console.WriteLine($"[{clientId}] 异常: {ex.Message}");
}
finally
{
client.Close();
_clients.TryRemove(clientId, out _);
Console.WriteLine($"客户端断开: {clientId}");
}
}
static void PrintStats()
{
var stats = _clients.Values
.GroupBy(c => "全部")
.Select(g => new
{
TotalClients = g.Count(),
TotalBytes = g.Sum(c => c.TotalBytes),
AvgDuration = g.Average(c => (DateTime.Now - c.ConnectTime).TotalSeconds)
})
.FirstOrDefault();
if (stats != null)
Console.WriteLine($"[统计] 当前连接: {stats.TotalClients}, 总流量: {stats.TotalBytes}字节, 平均时长: {stats.AvgDuration:F1}秒");
}
}
class ClientInfo
{
public string Id { get; set; } = "";
public DateTime ConnectTime { get; set; }
public int TotalBytes { get; set; }
}
总结
本文系统讲解了 C# 中的三大核心技术模块:
多线程
Thread适合精细控制,但开销大;ThreadPool适合短任务,自动管理;Task+async/await是现代异步编程首选;- 共享资源必须使用
lock等同步机制保证线程安全。
网络编程
- UDP 无连接、不可靠但高效,适合实时性场景;
- TCP 面向连接、可靠传输,适合数据完整性要求高的场景;
- TCP 需注意粘包问题,推荐使用长度前缀方案;
- 服务端应使用异步 + 多线程处理并发连接。
LINQ
- 提供查询语法和方法语法两种写法;
- 掌握
Where、Select、GroupBy、OrderBy、Join等核心方法; - 理解延迟执行与立即执行的区别;
- 善用聚合方法(
Count、Sum、Average等)简化统计。
这三大技术在实际开发中经常组合使用,掌握它们能显著提升 C# 开发能力和代码质量。
如果本文对你有帮助,欢迎点赞、收藏、关注!有任何问题欢迎在评论区交流。
C# 核心知识精讲:多线程、网络编程(UDP/TCP)与 LINQ 方法全解析
本文系统梳理 C# 开发中四大核心模块:多线程编程、UDP 网络通信、TCP 网络通信以及 LINQ 数据查询方法。每个模块均配有概念讲解与可运行代码示例,适合学习复习与面试备考。
前言
在 C# 开发中,多线程、网络编程和 LINQ 是三个高频使用且相互关联的核心技术领域:
- 多线程是提升程序并发性能、避免 UI 阻塞的基础;
- **网络编程(UDP/TCP)**是实现分布式通信、客户端-服务器架构的关键;
- LINQ 则极大简化了数据查询与集合操作,是 C# 开发者必备技能。
本文将从基础概念出发,结合实际代码示例,逐一深入讲解这三大模块。
一、C# 多线程编程
1.1 多线程基础概念
进程(Process):操作系统资源分配的基本单位,一个应用程序运行时就是一个进程。
线程(Thread):CPU 调度的最小单位,一个进程可以包含多个线程,它们共享进程的内存资源。
多线程的优势:
- 提高程序响应速度(UI 线程不被阻塞)
- 充分利用多核 CPU 资源
- 并行处理耗时任务(IO、计算等)
1.2 Thread 类
System.Threading.Thread 是最基础的线程类,用于创建和控制线程。
using System;
using System.Threading;
class ThreadDemo
{
static void Main()
{
// 创建线程
Thread t = new Thread(Worker);
t.Name = "工作线程1";
t.IsBackground = true; // 设置为后台线程
t.Start(); // 启动线程
// 主线程继续执行
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"主线程: {i}");
Thread.Sleep(100);
}
t.Join(); // 等待工作线程完成
Console.WriteLine("程序结束");
}
static void Worker()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"{Thread.CurrentThread.Name}: {i}");
Thread.Sleep(150);
}
}
}
常用属性与方法:
| 成员 | 说明 |
|---|---|
Name |
获取或设置线程名称 |
IsBackground |
获取或设置是否为后台线程 |
IsAlive |
获取线程是否处于活动状态 |
Start() |
启动线程 |
Sleep(int) |
静态方法,暂停当前线程指定毫秒 |
Join() |
等待线程终止 |
Abort() |
终止线程(已不推荐使用) |
1.3 线程池 ThreadPool
线程池维护一组工作线程,避免频繁创建和销毁线程带来的开销。
using System;
using System.Threading;
class ThreadPoolDemo
{
static void Main()
{
// 将工作项排入线程池
for (int i = 0; i < 5; i++)
{
ThreadPool.QueueUserWorkItem(Worker, i);
}
Console.WriteLine("主线程继续运行...");
Thread.Sleep(2000); // 等待线程池任务完成
Console.WriteLine("所有任务完成");
}
static void Worker(object state)
{
int taskId = (int)state;
Console.WriteLine($"任务 {taskId} 在线程 {Thread.CurrentThread.ManagedThreadId} 上执行");
Thread.Sleep(500);
}
}
线程池特点:
- 线程复用,减少创建销毁开销
- 自动管理线程数量
- 适合短时间运行的任务
- 不适合长时间运行的任务(会占用池资源)
1.4 Task 与 async/await
Task 和 Task<T> 是 .NET 推荐的异步编程模型,配合 async/await 关键字使用。
using System;
using System.Threading.Tasks;
class TaskDemo
{
static async Task Main()
{
Console.WriteLine("开始异步操作...");
// 方式一:Task.Run
Task<string> task1 = Task.Run(() =>
{
Task.Delay(1000).Wait();
return "Task.Run 结果";
});
// 方式二:async 方法
string result2 = await DoWorkAsync();
string result1 = await task1;
Console.WriteLine(result1);
Console.WriteLine(result2);
// 并行等待多个任务
Task[] tasks = new Task[]
{
Task.Delay(500),
Task.Delay(800),
Task.Delay(300)
};
await Task.WhenAll(tasks);
Console.WriteLine("所有并行任务完成");
}
static async Task<string> DoWorkAsync()
{
await Task.Delay(1500);
return "async 方法结果";
}
}
Task 常用方法:
| 方法 | 说明 |
|---|---|
Task.Run() |
将工作排入线程池并返回 Task |
Task.Delay() |
异步延迟 |
Task.WhenAll() |
等待所有任务完成 |
Task.WhenAny() |
等待任一任务完成 |
ContinueWith() |
任务完成后继续执行 |
Result |
同步获取结果(可能死锁,慎用) |
1.5 线程安全与锁
多线程环境下,共享资源的访问需要同步控制,否则会出现数据竞争。
using System;
using System.Threading;
using System.Threading.Tasks;
class LockDemo
{
private static int _counter = 0;
private static readonly object _lockObj = new object();
static async Task Main()
{
// 不加锁的情况
Task[] tasks = new Task[10];
for (int i = 0; i < 10; i++)
{
tasks[i] = Task.Run(() =>
{
for (int j = 0; j < 1000; j++)
{
lock (_lockObj) // 加锁保证线程安全
{
_counter++;
}
}
});
}
await Task.WhenAll(tasks);
Console.WriteLine($"最终计数: {_counter}"); // 预期 10000
}
}
常用同步机制:
| 机制 | 说明 |
|---|---|
lock |
最常用的互斥锁,基于 Monitor |
Monitor |
提供 Enter/Exit/Pulse/Wait 等方法 |
Mutex |
跨进程互斥锁 |
Semaphore |
信号量,限制并发数量 |
ReaderWriterLockSlim |
读写分离锁,多读单写 |
Interlocked |
原子操作(递增、交换等) |
ConcurrentQueue<T> 等 |
线程安全集合 |
二、C# 网络编程 — UDP
2.1 UDP 简介
UDP(User Datagram Protocol,用户数据报协议) 是一种无连接的传输层协议:
- 无连接:发送数据前不需要建立连接
- 不可靠:不保证数据送达、不保证顺序、不重传
- 开销小:头部只有 8 字节,传输效率高
- 支持广播和多播
适用场景:视频直播、DNS 查询、游戏实时通信、物联网传感器数据等对实时性要求高、可容忍少量丢包的场景。
2.2 UdpClient 类
System.Net.Sockets.UdpClient 是 .NET 中用于 UDP 通信的封装类。
常用方法:
| 方法 | 说明 |
|---|---|
UdpClient(int port) |
绑定到指定端口 |
Send(byte[], int, IPEndPoint) |
发送数据报 |
Receive(ref IPEndPoint) |
接收数据报(阻塞) |
ReceiveAsync() |
异步接收数据报 |
Close() |
关闭连接 |
2.3 UDP 服务端示例
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class UdpServer
{
static async Task Main(string[] args)
{
int port = 8888;
UdpClient udpServer = new UdpClient(port);
Console.WriteLine($"UDP 服务端已启动,监听端口 {port}...");
try
{
while (true)
{
// 异步接收数据
UdpReceiveResult result = await udpServer.ReceiveAsync();
string message = Encoding.UTF8.GetString(result.Buffer);
IPEndPoint remoteEndPoint = result.RemoteEndPoint;
Console.WriteLine($"收到来自 {remoteEndPoint} 的消息: {message}");
// 回复客户端
string reply = $"服务端已收到: {message}";
byte[] replyBytes = Encoding.UTF8.GetBytes(reply);
await udpServer.SendAsync(replyBytes, replyBytes.Length, remoteEndPoint);
}
}
catch (Exception ex)
{
Console.WriteLine($"服务端异常: {ex.Message}");
}
finally
{
udpServer.Close();
}
}
}
2.4 UDP 客户端示例
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class UdpClientDemo
{
static async Task Main(string[] args)
{
string serverIp = "127.0.0.1";
int serverPort = 8888;
UdpClient client = new UdpClient();
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(serverIp), serverPort);
try
{
// 发送消息
string message = "Hello, UDP Server!";
byte[] data = Encoding.UTF8.GetBytes(message);
await client.SendAsync(data, data.Length, serverEndPoint);
Console.WriteLine($"已发送: {message}");
// 接收回复(设置超时)
client.Client.ReceiveTimeout = 3000;
UdpReceiveResult result = await client.ReceiveAsync();
string reply = Encoding.UTF8.GetString(result.Buffer);
Console.WriteLine($"收到回复: {reply}");
}
catch (SocketException ex)
{
Console.WriteLine($"网络异常: {ex.Message}");
}
finally
{
client.Close();
}
}
}
2.5 UDP 广播示例
UDP 支持向同一网段内所有主机发送广播消息。
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class UdpBroadcast
{
static async Task Main(string[] args)
{
int port = 9000;
UdpClient broadcaster = new UdpClient();
broadcaster.EnableBroadcast = true; // 启用广播
IPEndPoint broadcastEndPoint = new IPEndPoint(IPAddress.Broadcast, port);
for (int i = 0; i < 5; i++)
{
string msg = $"广播消息 #{i + 1} - {DateTime.Now:HH:mm:ss}";
byte[] data = Encoding.UTF8.GetBytes(msg);
await broadcaster.SendAsync(data, data.Length, broadcastEndPoint);
Console.WriteLine($"已广播: {msg}");
await Task.Delay(1000);
}
broadcaster.Close();
Console.WriteLine("广播结束");
}
}
三、C# 网络编程 — TCP
3.1 TCP 简介
TCP(Transmission Control Protocol,传输控制协议) 是一种面向连接的、可靠的传输层协议:
- 面向连接:通信前需通过三次握手建立连接
- 可靠传输:保证数据有序、无差错、不丢失、不重复
- 流量控制与拥塞控制:自动调整发送速率
- 全双工:连接建立后双方可同时收发数据
适用场景:文件传输(FTP/HTTP)、邮件(SMTP)、远程登录(SSH)、数据库连接等要求数据完整性的场景。
3.2 TcpListener 与 TcpClient
| 类 | 用途 |
|---|---|
TcpListener |
TCP 服务端,监听客户端连接请求 |
TcpClient |
TCP 客户端,用于连接服务端并收发数据 |
NetworkStream |
网络数据流,用于读写数据 |
3.3 TCP 服务端示例
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
class TcpServer
{
static async Task Main(string[] args)
{
IPAddress ip = IPAddress.Any;
int port = 8888;
TcpListener listener = new TcpListener(ip, port);
listener.Start();
Console.WriteLine($"TCP 服务端已启动,监听 {ip}:{port}");
try
{
while (true)
{
// 等待客户端连接
TcpClient client = await listener.AcceptTcpClientAsync();
Console.WriteLine($"客户端已连接: {client.Client.RemoteEndPoint}");
// 每个客户端独立线程处理
_ = HandleClientAsync(client);
}
}
catch (Exception ex)
{
Console.WriteLine($"服务端异常: {ex.Message}");
}
finally
{
listener.Stop();
}
}
static async Task HandleClientAsync(TcpClient client)
{
try
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
while (true)
{
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0) // 客户端断开
{
Console.WriteLine("客户端断开连接");
break;
}
string received = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"收到: {received}");
// 回显(Echo)
byte[] reply = Encoding.UTF8.GetBytes($"服务端回显: {received}");
await stream.WriteAsync(reply, 0, reply.Length);
}
}
catch (Exception ex)
{
Console.WriteLine($"处理客户端异常: {ex.Message}");
}
finally
{
client.Close();
}
}
}
3.4 TCP 客户端示例
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
class TcpClientDemo
{
static async Task Main(string[] args)
{
string serverIp = "127.0.0.1";
int port = 8888;
TcpClient client = new TcpClient();
try
{
// 连接服务端
await client.ConnectAsync(serverIp, port);
Console.WriteLine($"已连接到服务端 {serverIp}:{port}");
NetworkStream stream = client.GetStream();
// 发送消息
string message = "Hello, TCP Server!";
byte[] data = Encoding.UTF8.GetBytes(message);
await stream.WriteAsync(data, 0, data.Length);
Console.WriteLine($"已发送: {message}");
// 接收回复
byte[] buffer = new byte[1024];
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
string reply = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"收到: {reply}");
}
catch (SocketException ex)
{
Console.WriteLine($"连接失败: {ex.Message}");
}
finally
{
client.Close();
}
}
}
3.5 TCP 粘包问题与解决方案
TCP 是面向流的协议,数据没有明确的边界,可能出现粘包(多个数据包合并)或拆包(一个数据包被拆分)的情况。
常见解决方案:
- 固定长度:每条消息固定长度
- 特殊分隔符:用特定字符(如
\n、\r\n)分隔 - 长度前缀:消息前加 4 字节表示消息长度(推荐)
// 长度前缀方式发送消息
public static async Task SendMessageAsync(NetworkStream stream, string message)
{
byte[] body = Encoding.UTF8.GetBytes(message);
byte[] lengthPrefix = BitConverter.GetBytes(body.Length);
// 先发送长度(4字节),再发送内容
await stream.WriteAsync(lengthPrefix, 0, 4);
await stream.WriteAsync(body, 0, body.Length);
}
// 长度前缀方式接收消息
public static async Task<string> ReceiveMessageAsync(NetworkStream stream)
{
byte[] lengthBuffer = new byte[4];
int totalRead = 0;
// 读取4字节长度
while (totalRead < 4)
{
int read = await stream.ReadAsync(lengthBuffer, totalRead, 4 - totalRead);
if (read == 0) return null;
totalRead += read;
}
int bodyLength = BitConverter.ToInt32(lengthBuffer, 0);
byte[] bodyBuffer = new byte[bodyLength];
totalRead = 0;
// 读取完整消息体
while (totalRead < bodyLength)
{
int read = await stream.ReadAsync(bodyBuffer, totalRead, bodyLength - totalRead);
if (read == 0) return null;
totalRead += read;
}
return Encoding.UTF8.GetString(bodyBuffer);
}
3.6 UDP 与 TCP 对比
| 特性 | UDP | TCP |
|---|---|---|
| 连接性 | 无连接 | 面向连接 |
| 可靠性 | 不可靠 | 可靠 |
| 顺序性 | 不保证顺序 | 保证顺序 |
| 传输速度 | 快 | 相对较慢 |
| 头部开销 | 8 字节 | 20 字节(不含选项) |
| 流量控制 | 无 | 有 |
| 拥塞控制 | 无 | 有 |
| 广播/多播 | 支持 | 不支持 |
| 适用场景 | 实时音视频、游戏、DNS | 文件传输、Web、邮件 |
四、LINQ 方法详解
4.1 LINQ 简介
LINQ(Language Integrated Query,语言集成查询) 是 C# 3.0 引入的一项特性,允许使用类似 SQL 的语法直接在 C# 代码中查询数据。
LINQ 的两种语法:
- 查询语法(Query Syntax):类似 SQL,使用
from、where、select等关键字 - 方法语法(Method Syntax):使用扩展方法链式调用
// 查询语法
var querySyntax = from s in students
where s.Age > 18
orderby s.Name
select s;
// 方法语法(等价)
var methodSyntax = students
.Where(s => s.Age > 18)
.OrderBy(s => s.Name)
.Select(s => s);
4.2 数据准备
以下示例使用如下测试数据:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Major { get; set; }
public decimal Score { get; set; }
}
List<Student> students = new List<Student>
{
new Student { Id = 1, Name = "张三", Age = 20, Major = "计算机", Score = 85.5m },
new Student { Id = 2, Name = "李四", Age = 22, Major = "数学", Score = 92.0m },
new Student { Id = 3, Name = "王五", Age = 19, Major = "计算机", Score = 78.5m },
new Student { Id = 4, Name = "赵六", Age = 21, Major = "英语", Score = 88.0m },
new Student { Id = 5, Name = "钱七", Age = 20, Major = "数学", Score = 95.5m },
new Student { Id = 6, Name = "孙八", Age = 23, Major = "计算机", Score = 67.0m }
};
4.3 筛选与投影
Where — 条件筛选
// 筛选计算机专业且分数大于80的学生
var result = students.Where(s => s.Major == "计算机" && s.Score > 80);
foreach (var s in result)
Console.WriteLine($"{s.Name} - {s.Major} - {s.Score}");
Select — 投影(转换)
// 只选取姓名和分数,投影为匿名类型
var names = students.Select(s => new { s.Name, s.Score });
foreach (var n in names)
Console.WriteLine($"{n.Name}: {n.Score}");
// Select 带索引重载
var indexed = students.Select((s, index) => $"{index + 1}. {s.Name}");
SelectMany — 扁平化集合
// 将嵌套集合扁平化为单一序列
List<string[]> courses = new List<string[]>
{
new string[] { "C#", "Java" },
new string[] { "Python", "Go", "Rust" }
};
var allCourses = courses.SelectMany(c => c);
// 结果: C#, Java, Python, Go, Rust
4.4 排序
OrderBy / OrderByDescending
// 按分数升序
var ascending = students.OrderBy(s => s.Score);
// 按分数降序
var descending = students.OrderByDescending(s => s.Score);
ThenBy / ThenByDescending — 多级排序
// 先按专业升序,再按分数降序
var multiSort = students
.OrderBy(s => s.Major)
.ThenByDescending(s => s.Score);
Reverse — 反转序列
var reversed = students.Reverse();
4.5 分组
GroupBy — 分组
// 按专业分组
var groups = students.GroupBy(s => s.Major);
foreach (var group in groups)
{
Console.WriteLine($"专业: {group.Key},人数: {group.Count()}");
foreach (var s in group)
Console.WriteLine($" - {s.Name} ({s.Score})");
}
ToLookup — 生成查找表
// ToLookup 生成 ILookup<TKey, TElement>,类似字典但值是集合
var lookup = students.ToLookup(s => s.Major);
foreach (var s in lookup["计算机"])
Console.WriteLine(s.Name);
4.6 集合操作
Distinct — 去重
int[] numbers = { 1, 2, 2, 3, 3, 3, 4 };
var unique = numbers.Distinct(); // 1, 2, 3, 4
Union / Intersect / Except
int[] a = { 1, 2, 3, 4 };
int[] b = { 3, 4, 5, 6 };
var union = a.Union(b); // 并集: 1,2,3,4,5,6
var intersect = a.Intersect(b); // 交集: 3,4
var except = a.Except(b); // 差集(a有b无): 1,2
Concat — 连接序列
var concat = a.Concat(b); // 1,2,3,4,3,4,5,6(不去重)
4.7 聚合操作
| 方法 | 说明 | 示例 |
|---|---|---|
Count() |
元素数量 | students.Count() |
Count(predicate) |
满足条件的数量 | students.Count(s => s.Score > 80) |
Sum() |
求和 | students.Sum(s => s.Score) |
Average() |
平均值 | students.Average(s => s.Score) |
Max() |
最大值 | students.Max(s => s.Score) |
Min() |
最小值 | students.Min(s => s.Score) |
Aggregate() |
自定义聚合 | 见下方示例 |
// Aggregate 自定义聚合:计算所有分数的乘积(示例)
decimal product = students.Aggregate(1m, (acc, s) => acc * s.Score);
// Aggregate 拼接字符串
string allNames = students.Aggregate("", (acc, s) => acc + s.Name + ", ");
// 结果: "张三, 李四, 王五, 赵六, 钱七, 孙八, "
4.8 元素操作
| 方法 | 说明 | 异常行为 |
|---|---|---|
First() |
第一个元素 | 无元素抛异常 |
FirstOrDefault() |
第一个元素或默认值 | 无元素返回默认值 |
Last() |
最后一个元素 | 无元素抛异常 |
LastOrDefault() |
最后一个或默认值 | 无元素返回默认值 |
Single() |
唯一元素 | 0个或多个都抛异常 |
SingleOrDefault() |
唯一元素或默认值 | 多个抛异常 |
ElementAt(int) |
指定索引元素 | 越界抛异常 |
ElementAtOrDefault(int) |
指定索引或默认值 | 越界返回默认值 |
var first = students.First(s => s.Major == "计算机"); // 张三
var firstOrDefault = students.FirstOrDefault(s => s.Major == "物理"); // null
var single = students.Single(s => s.Id == 1); // 张三
// students.Single(s => s.Major == "计算机") // 抛异常(有多个)
4.9 量词操作
| 方法 | 说明 |
|---|---|
Any() |
是否有元素 |
Any(predicate) |
是否有满足条件的元素 |
All(predicate) |
是否所有元素都满足条件 |
Contains(value) |
是否包含指定元素 |
bool hasHighScore = students.Any(s => s.Score > 90); // true
bool allPassed = students.All(s => s.Score >= 60); // true
bool hasZhang = students.Select(s => s.Name).Contains("张三"); // true
4.10 分区操作
| 方法 | 说明 |
|---|---|
Take(n) |
取前 n 个元素 |
Skip(n) |
跳过前 n 个元素 |
TakeWhile(predicate) |
取满足条件的元素,直到不满足 |
SkipWhile(predicate) |
跳过满足条件的元素,直到不满足 |
var top3 = students.OrderByDescending(s => s.Score).Take(3); // 前三名
var page2 = students.OrderBy(s => s.Id).Skip(3).Take(3); // 分页第二页
4.11 连接操作
Join — 内连接
// 准备第二个数据源
var departments = new List<Department>
{
new Department { Major = "计算机", DeptName = "信息工程学院" },
new Department { Major = "数学", DeptName = "理学院" },
new Department { Major = "英语", DeptName = "外国语学院" }
};
// 内连接:学生与学院信息关联
var joinResult = students.Join(
departments,
s => s.Major, // 左表键
d => d.Major, // 右表键
(s, d) => new { s.Name, s.Major, d.DeptName }
);
foreach (var item in joinResult)
Console.WriteLine($"{item.Name} - {item.Major} - {item.DeptName}");
GroupJoin — 分组连接(左外连接)
var groupJoin = departments.GroupJoin(
students,
d => d.Major,
s => s.Major,
(d, studentGroup) => new
{
d.DeptName,
Students = studentGroup.ToList()
}
);
4.12 转换操作
| 方法 | 说明 |
|---|---|
ToList() |
转为 List<T> |
ToArray() |
转为数组 |
ToDictionary() |
转为字典 |
ToHashSet() |
转为 HashSet |
Cast<T>() |
强制转换元素类型 |
OfType<T>() |
筛选指定类型元素 |
AsEnumerable() |
转为 IEnumerable<T> |
AsQueryable() |
转为 IQueryable<T> |
// ToDictionary
var dict = students.ToDictionary(s => s.Id, s => s.Name);
// OfType 筛选类型
object[] mixed = { 1, "hello", 2, "world", 3 };
var strings = mixed.OfType<string>(); // "hello", "world"
var ints = mixed.OfType<int>(); // 1, 2, 3
4.13 生成操作
| 方法 | 说明 |
|---|---|
Enumerable.Range(start, count) |
生成整数序列 |
Enumerable.Repeat(element, count) |
重复元素生成序列 |
Enumerable.Empty<T>() |
空序列 |
var range = Enumerable.Range(1, 10); // 1,2,...,10
var repeat = Enumerable.Repeat("A", 5); // A,A,A,A,A
var empty = Enumerable.Empty<int>(); // 空序列
4.14 延迟执行 vs 立即执行
延迟执行(Deferred Execution):查询在迭代时才执行,每次迭代重新计算。
var query = students.Where(s => s.Score > 80); // 此时未执行
students.Add(new Student { Id = 7, Name = "周九", Age = 20, Major = "计算机", Score = 90 });
foreach (var s in query) // 迭代时执行,包含新添加的周九
Console.WriteLine(s.Name);
立即执行(Immediate Execution):调用时立即执行并缓存结果。
var list = students.Where(s => s.Score > 80).ToList(); // 立即执行
students.Add(new Student { Id = 8, Name = "吴十", Age = 20, Major = "计算机", Score = 91 });
// list 中不包含吴十,因为已经执行完毕
判断规则:
- 返回
IEnumerable<T>/IQueryable<T>→ 通常延迟执行 - 返回单个值(
Count、Sum、First、Any等)或集合(ToList、ToArray、ToDictionary)→ 立即执行
4.15 LINQ 综合实战示例
// 需求:统计每个专业的平均分、最高分、人数,按平均分降序排列
var report = students
.GroupBy(s => s.Major)
.Select(g => new
{
Major = g.Key,
Count = g.Count(),
AvgScore = g.Average(s => s.Score),
MaxScore = g.Max(s => s.Score),
TopStudent = g.OrderByDescending(s => s.Score).First().Name
})
.OrderByDescending(r => r.AvgScore)
.ToList();
Console.WriteLine("| 专业 | 人数 | 平均分 | 最高分 | 最高分学生 |");
Console.WriteLine("|------|------|--------|--------|------------|");
foreach (var r in report)
Console.WriteLine($"| {r.Major} | {r.Count} | {r.AvgScore:F1} | {r.MaxScore} | {r.TopStudent} |");
五、多线程 + 网络编程 + LINQ 综合应用
下面是一个综合示例:使用多线程并发处理多个 TCP 客户端连接,并用 LINQ 统计连接信息。
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
class IntegratedServer
{
// 线程安全的连接信息集合
private static ConcurrentDictionary<string, ClientInfo> _clients = new();
static async Task Main(string[] args)
{
TcpListener listener = new TcpListener(IPAddress.Any, 8888);
listener.Start();
Console.WriteLine("综合服务端已启动...");
// 后台定时统计
_ = Task.Run(async () =>
{
while (true)
{
await Task.Delay(5000);
PrintStats();
}
});
while (true)
{
TcpClient client = await listener.AcceptTcpClientAsync();
string clientId = client.Client.RemoteEndPoint!.ToString()!;
_clients[clientId] = new ClientInfo { Id = clientId, ConnectTime = DateTime.Now };
Console.WriteLine($"客户端连接: {clientId}");
_ = HandleClientAsync(client, clientId);
}
}
static async Task HandleClientAsync(TcpClient client, string clientId)
{
try
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
int totalBytes = 0;
while (true)
{
int read = await stream.ReadAsync(buffer, 0, buffer.Length);
if (read == 0) break;
totalBytes += read;
string msg = Encoding.UTF8.GetString(buffer, 0, read);
Console.WriteLine($"[{clientId}] {msg}");
byte[] reply = Encoding.UTF8.GetBytes($"已收到 {read} 字节");
await stream.WriteAsync(reply, 0, reply.Length);
}
if (_clients.TryGetValue(clientId, out var info))
info.TotalBytes = totalBytes;
}
catch (Exception ex)
{
Console.WriteLine($"[{clientId}] 异常: {ex.Message}");
}
finally
{
client.Close();
_clients.TryRemove(clientId, out _);
Console.WriteLine($"客户端断开: {clientId}");
}
}
static void PrintStats()
{
var stats = _clients.Values
.GroupBy(c => "全部")
.Select(g => new
{
TotalClients = g.Count(),
TotalBytes = g.Sum(c => c.TotalBytes),
AvgDuration = g.Average(c => (DateTime.Now - c.ConnectTime).TotalSeconds)
})
.FirstOrDefault();
if (stats != null)
Console.WriteLine($"[统计] 当前连接: {stats.TotalClients}, 总流量: {stats.TotalBytes}字节, 平均时长: {stats.AvgDuration:F1}秒");
}
}
class ClientInfo
{
public string Id { get; set; } = "";
public DateTime ConnectTime { get; set; }
public int TotalBytes { get; set; }
}
总结
本文系统讲解了 C# 中的三大核心技术模块:
多线程
Thread适合精细控制,但开销大;ThreadPool适合短任务,自动管理;Task+async/await是现代异步编程首选;- 共享资源必须使用
lock等同步机制保证线程安全。
网络编程
- UDP 无连接、不可靠但高效,适合实时性场景;
- TCP 面向连接、可靠传输,适合数据完整性要求高的场景;
- TCP 需注意粘包问题,推荐使用长度前缀方案;
- 服务端应使用异步 + 多线程处理并发连接。
LINQ
- 提供查询语法和方法语法两种写法;
- 掌握
Where、Select、GroupBy、OrderBy、Join等核心方法; - 理解延迟执行与立即执行的区别;
- 善用聚合方法(
Count、Sum、Average等)简化统计。
这三大技术在实际开发中经常组合使用,掌握它们能显著提升 C# 开发能力和代码质量。
最后附赠一张思维导图:
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)