C# 核心编程知识点总结:网络编程、委托与事件、异步编程与LINQ

一份系统性梳理C#进阶编程核心知识的博客,涵盖Socket网络通信、委托与事件机制、Task异步编程模型以及LINQ查询技术


一、网络编程(Socket编程)

1.1 什么是网络编程?

网络编程就是通过网络进行设备与设备之间的数据传输,也称为 Socket网络编程。Socket是网络通信的核心接口,提供了一组方法和属性,使开发者可以轻松地创建连接、发送和接收数据。

1.2 网络通信模型

网络通信遵循分层模型,常见的有OSI七层模型和TCP/IP四层模型:

flowchart TD subgraph OSI["OSI七层模型"] A1[应用层] --> A2[表示层] --> A3[会话层] --> A4[传输层] --> A5[网络层] --> A6[数据链路层] --> A7[物理层] end subgraph TCPIP["TCP/IP四层模型"] B1[应用层] --> B4[传输层] --> B5[网络层] --> B6[网络接口层] end A1 -.-> B1 A2 -.-> B1 A3 -.-> B1 A4 -.-> B4 A5 -.-> B5 A6 -.-> B6 A7 -.-> B6

1.3 网络通信三要素

要素 说明
协议 约束数据如何发送和格式化
IP地址 标识数据要发送的目标设备
端口号 标识数据要发送到目标设备的哪个应用程序

1.4 IPAddress类

IPAddress类位于System.Net命名空间,用于表示和操作IP地址:

csharp 复制代码
using System.Net;

// 解析IP地址
IPAddress ip = IPAddress.Parse("127.0.0.1");

// 获取回送地址
IPAddress loopback = IPAddress.Loopback;

// 获取任意IP地址
IPAddress any = IPAddress.Any;

// 获取地址族
Console.WriteLine(ip.AddressFamily);  // InterNetwork

// 获取字节数组
byte[] bytes = ip.GetAddressBytes();

1.5 UDP协议(无连接通信)

UDP是无连接协议,发送数据前不建立逻辑连接,通信效率高,但不保证数据完整性。

sequenceDiagram participant Client as UDP客户端 participant Server as UDP服务器 Client->>Server: Send() 发送数据报文 Server-->>Client: Receive() 接收数据报文 Note over Client,Server: 无需建立连接,不保证可靠性

UdpClient示例

csharp 复制代码
// UDP服务器
UdpClient server = new UdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8888));
IPEndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);

byte[] data = server.Receive(ref clientEP);
string msg = Encoding.UTF8.GetString(data);

// UDP客户端
UdpClient client = new UdpClient();
IPEndPoint serverEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8888);
byte[] data = Encoding.UTF8.GetBytes("Hello Server");
client.Send(data, data.Length, serverEP);

1.6 TCP协议(面向连接通信)

TCP是面向连接的协议,通信前必须建立连接,保证数据传输的可靠性。

sequenceDiagram participant Client as TCP客户端 participant Server as TCP服务器 Server->>Server: TcpListener.Start() 监听 Client->>Server: TcpClient.Connect() 连接请求 Server->>Server: AcceptTcpClient() 接受连接 Server->>Client: 建立连接 Client->>Server: NetworkStream 发送数据 Server-->>Client: NetworkStream 响应数据 Client->>Server: Close() 关闭连接

TCP服务端示例

csharp 复制代码
using System.Net.Sockets;
using System.Net;

TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 8888);
listener.Start();

TcpClient client = listener.AcceptTcpClient();
NetworkStream stream = client.GetStream();

// 接收数据
byte[] buffer = new byte[1024];
int bytesRead = stream.Read(buffer, 0, buffer.Length);
string request = Encoding.UTF8.GetString(buffer, 0, bytesRead);

// 发送响应
byte[] response = Encoding.UTF8.GetBytes("Hello Client");
stream.Write(response, 0, response.Length);

client.Close();
listener.Stop();

1.7 TCP与UDP对比

特性 TCP UDP
连接方式 面向连接 无连接
可靠性 可靠传输 不可靠传输
速度 较慢 较快
应用场景 文件传输、网页访问 视频会议、实时游戏

二、委托与事件

2.1 什么是委托?

委托是C#中的引用类型,专门用于存储方法的引用(内存地址)。它约束一类方法的签名规则(返回值类型 + 参数个数、类型、顺序)。

flowchart LR subgraph 代码区 Add[&#34;Add() 方法&#34;] Multiply[&#34;Multiply() 方法&#34;] end subgraph 堆内存 DelegateInstance[&#34;委托实例<br>存储方法地址&#34;] end subgraph 栈内存 CalVar[&#34;cal 变量&#34;] end CalVar -->|指向| DelegateInstance DelegateInstance -->|存储地址| Add DelegateInstance -.->|存储地址| Multiply style 代码区 fill:#e8f5e9 style 堆内存 fill:#fff3e0 style 栈内存 fill:#e3f2fd

2.2 委托的定义与使用

csharp 复制代码
// 定义委托类型
public delegate int Calculate(int x, int y);

// 匹配委托签名的方法
static int Add(int x, int y) => x + y;
static int Multiply(int x, int y) => x * y;

// 使用委托
Calculate cal = Add;        // 方法组简写
int result = cal(3, 5);     // 调用委托

cal = Multiply;
result = cal.Invoke(3, 5);  // 使用Invoke调用

2.3 委托的意义

  1. 实现回调函数:任务完成后自动执行外部方法
  2. 代码解耦:通用代码与业务逻辑分离
  3. 动态行为传递:运行时切换执行逻辑
  4. 统一管理回调:可在委托调用前后增加统一逻辑

2.4 多播委托

委托继承自MulticastDelegate,天然支持多播特性:

flowchart LR subgraph 主委托[&#34;主委托(多播)&#34;] direction TB M1[&#34;子委托1&#34;] --> M2[&#34;子委托2&#34;] --> M3[&#34;子委托3&#34;] end M1 -.->|绑定| Add[&#34;Add()&#34;] M2 -.->|绑定| Multiply[&#34;Multiply()&#34;] M3 -.->|绑定| Subtract[&#34;Subtract()&#34;] Invoke[&#34;调用主委托&#34;] --> M1 & M2 & M3 Invoke -->|按顺序执行| Result[&#34;全部方法依次执行&#34;]
csharp 复制代码
Calculate cal = Add;
cal += Multiply;     // 追加方法
cal += Subtract;     // 再追加

cal(5, 3);  // 依次执行 Add → Multiply → Subtract

// 移除方法
cal -= Multiply;

// 注意:多播委托直接调用只返回最后一个方法的返回值
// 要获取全部返回值,使用GetInvocationList()
foreach (Calculate sub in cal.GetInvocationList())
{
    int result = sub(5, 3);
    Console.WriteLine(result);
}

2.5 Action和Func

系统提供的预定义委托,无需手动声明:

委托类型 返回值 参数数量 使用场景
Action void 0~16个 纯执行行为,无需返回
Func<TResult> 有返回值 0~15个 需要获取返回结果
csharp 复制代码
// Action - 无返回值
Action log = () => Console.WriteLine("日志记录");
Action<int, string> print = (age, name) => Console.WriteLine($"{name}今年{age}岁");

// Func - 有返回值
Func<int, int, int> add = (a, b) => a + b;
int sum = add(3, 5);  // 8

Func<string> getMsg = () => "Hello World";

2.6 事件(Event)

事件是封装保护后的多播委托,遵循"发布-订阅"设计模式:

flowchart LR subgraph 发布者 Publisher[&#34;Button 发布者&#34;] --> Event[&#34;OnClick 事件&#34;] end subgraph 订阅者1[&#34;订阅者1&#34;] Handler1[&#34;HandleClick()&#34;] end subgraph 订阅者2[&#34;订阅者2&#34;] Handler2[&#34;LogClick()&#34;] end Event -->|通知| Handler1 Event -->|通知| Handler2 Note[&#34;⚠️ 事件只能在发布者内部触发<br>外部只能订阅/取消订阅&#34;]

事件与普通委托的区别

特性 普通委托 事件(event)
赋值操作 支持 = 覆盖 禁止 = 赋值
调用权限 类内外都可调用 在声明类内部调用
封装安全性 较低 较高
csharp 复制代码
// 定义事件
public class Button
{
    public delegate void ClickHandler();
    public event ClickHandler OnClick;

    public void Click()
    {
        Console.WriteLine("按钮被点击");
        OnClick?.Invoke();  // 只能在类内部触发
    }
}

// 订阅事件
Button btn = new Button();
btn.OnClick += () => Console.WriteLine("开始游戏");
btn.OnClick += () => Console.WriteLine("记录日志");

btn.Click();  // 触发事件

2.7 标准事件写法(EventHandler)

csharp 复制代码
// 自定义事件参数
public class ButtonClickArgs : EventArgs
{
    public string ButtonName { get; set; }
    public DateTime ClickTime { get; set; }
}

// 发布者
public class GameButton
{
    public event EventHandler<ButtonClickArgs> OnClick;

    public void Click()
    {
        var args = new ButtonClickArgs
        {
            ButtonName = "开始按钮",
            ClickTime = DateTime.Now
        };
        OnClick?.Invoke(this, args);
    }
}

// 订阅者
GameButton btn = new GameButton();
btn.OnClick += (sender, e) =>
{
    Console.WriteLine($"{e.ButtonName} 于 {e.ClickTime} 被点击");
};

三、异步编程(Task与async/await)

3.1 同步与异步

类型 特点
同步方法 调用时需要等待返回结果,才能继续执行
异步方法 调用时无须等待返回结果,可以继续执行
flowchart LR subgraph 同步 S1[&#34;任务A&#34;] -->|等待| S2[&#34;任务B&#34;] -->|等待| S3[&#34;任务C&#34;] end subgraph 异步 A1[&#34;任务A&#34;] --> A2[&#34;任务B&#34;] A1 -->|不等待| A3[&#34;任务C&#34;] A2 --> A4[&#34;任务D&#34;] A3 --> A4 end

3.2 Task的创建与启动

csharp 复制代码
// 方式1:new + Start
Task t1 = new Task(() => Console.WriteLine("任务1"));
t1.Start();

// 方式2:Task.Run(最常用)
Task t2 = Task.Run(() => Console.WriteLine("任务2"));

// 方式3:TaskFactory
Task t3 = Task.Factory.StartNew(() => Console.WriteLine("任务3"));

// 带返回值
Task<int> t4 = Task.Run(() =>
{
    int sum = 0;
    for (int i = 0; i < 100; i++) sum += i;
    return sum;
});
int result = t4.Result;  // 阻塞等待

3.3 async与await

csharp 复制代码
// 定义异步方法
async Task<int> GetSumAsync()
{
    await Task.Delay(1);  // 模拟异步操作
    int sum = 0;
    for (int i = 0; i < 100; i++) sum += i;
    return sum;
}

// 调用异步方法
async Task UseAsync()
{
    int result = await GetSumAsync();  // 等待但不阻塞
    Console.WriteLine($"结果: {result}");
}

// Main方法中调用
static async Task Main()
{
    await UseAsync();
}

3.4 任务等待与延续

flowchart TD T1[&#34;Task1&#34;] -->|Wait| WaitAll[&#34;阻塞等待所有任务&#34;] T2[&#34;Task2&#34;] -->|Wait| WaitAll T3[&#34;Task3&#34;] -->|Wait| WaitAll T1 -->|ContinueWith| CT1[&#34;Task1完成后执行&#34;] T2 -->|ContinueWith| CT2[&#34;Task2完成后执行&#34;] subgraph 非阻塞等待 WhenAny[&#34;Task.WhenAny&#34;] WhenAll[&#34;Task.WhenAll&#34;] end
csharp 复制代码
// 阻塞等待
Task.WaitAll(t1, t2, t3);  // 等待所有完成
Task.WaitAny(t1, t2, t3);  // 任意一个完成

// 非阻塞延续
Task.WhenAll(t1, t2).ContinueWith(t =>
{
    Console.WriteLine("所有任务完成");
});

// 使用await
await Task.WhenAll(t1, t2, t3);

3.5 取消任务

csharp 复制代码
using CancellationTokenSource cts = new CancellationTokenSource();

Task task = Task.Run(() =>
{
    for (int i = 0; i < 100; i++)
    {
        if (cts.Token.IsCancellationRequested)
        {
            Console.WriteLine("任务已取消");
            return;
        }
        Thread.Sleep(100);
    }
}, cts.Token);

// 取消任务
cts.Cancel();

3.6 更新UI的正确方式

在WinForm/WPF中,不能在非UI线程直接操作控件:

csharp 复制代码
// 错误方式 ❌
Task.Run(() =>
{
    textBox1.Text = "更新";  // 抛出异常
});

// 正确方式 ✅ 使用Invoke
Task.Run(() =>
{
    this.Invoke(new Action(() =>
    {
        textBox1.Text = "更新";
    }));
});

// 或使用BeginInvoke(异步)
this.BeginInvoke(new Action(() =>
{
    textBox1.Text = "更新";
}));

// Lambda简化
this.Invoke(new Action(() => textBox1.Text = "更新"));

Invoke vs BeginInvoke

方法 同步/异步 说明
Invoke 同步 等待UI线程执行完成,子线程才继续
BeginInvoke 异步 提交给UI线程后立即返回,子线程不等待

四、LINQ语言集成查询

4.1 什么是LINQ?

LINQ(Language Integrated Query)允许使用类似SQL的查询语法直接操作各种数据源。

flowchart TD LINQ[&#34;LINQ 语言集成查询&#34;] LINQ --> O[&#34;LINQ to Objects<br>(集合/数组)&#34;] LINQ --> S[&#34;LINQ to SQL<br>(数据库)&#34;] LINQ --> X[&#34;LINQ to XML<br>(XML文件)&#34;] LINQ --> D[&#34;LINQ to DataSet<br>(DataSet)&#34;] O --> Application[&#34;统一的查询语法<br>操作不同数据源&#34;] S --> Application X --> Application D --> Application

4.2 查询语法 vs 方法语法

csharp 复制代码
// 数据源
int[] numbers = { 1, 2, 5, 10, 15, 20, 25 };

// 查询语法
var result1 = from n in numbers
              where n > 10 && n % 2 == 0
              select n;

// 方法语法(推荐)
var result2 = numbers.Where(n => n > 10 && n % 2 == 0);

4.3 常用标准查询运算符

flowchart LR subgraph 筛选 Where[&#34;Where&#34;] OfType[&#34;OfType&#34;] end subgraph 投影 Select[&#34;Select&#34;] SelectMany[&#34;SelectMany&#34;] end subgraph 排序 OrderBy[&#34;OrderBy&#34;] ThenBy[&#34;ThenBy&#34;] Reverse[&#34;Reverse&#34;] end subgraph 分组 GroupBy[&#34;GroupBy&#34;] end subgraph 聚合 Count[&#34;Count&#34;] Sum[&#34;Sum&#34;] Avg[&#34;Average&#34;] Max[&#34;Max&#34;] Min[&#34;Min&#34;] end subgraph 联接 Join[&#34;Join&#34;] GroupJoin[&#34;GroupJoin&#34;] end

4.4 筛选:Where

csharp 复制代码
var students = new List<Student>
{
    new Student { Id = 1, Name = "张三", Age = 18 },
    new Student { Id = 2, Name = "李四", Age = 20 },
    new Student { Id = 3, Name = "王五", Age = 16 }
};

// 筛选年龄在12-20之间的学生
var teens = students.Where(s => s.Age > 12 && s.Age < 20);

// OfType:筛选特定类型
var persons = mixedList.OfType<Person>();

4.5 投影:Select / SelectMany

csharp 复制代码
// Select:提取属性或转换
var names = students.Select(s => s.Name);
var anonym = students.Select(s => new { s.Name, s.Age });

// SelectMany:扁平化嵌套集合
var classes = new List<Class>
{
    new Class { Students = new List<string> { "张三", "李四" } },
    new Class { Students = new List<string> { "王五", "赵六" } }
};

var allStudents = classes.SelectMany(c => c.Students);
// ["张三", "李四", "王五", "赵六"]

4.6 排序:OrderBy

csharp 复制代码
// 单级排序
var sorted = products.OrderBy(p => p.Price);

// 多级排序
var sorted2 = products
    .OrderBy(p => p.Category)
    .ThenByDescending(p => p.Price);

4.7 分组:GroupBy

csharp 复制代码
var grouped = products.GroupBy(p => p.Category);

foreach (var group in grouped)
{
    Console.WriteLine($"分类: {group.Key}");
    foreach (var product in group)
    {
        Console.WriteLine($"  {product.Name}: {product.Price:C}");
    }
}

4.8 联接:Join

csharp 复制代码
// 内连接
var result = from c in customers
             join o in orders on c.CId equals o.CId
             select new { c.Name, o.Total };

// 方法语法
var result2 = customers.Join(orders,
    c => c.CId,
    o => o.CId,
    (c, o) => new { c.Name, o.Total });

4.9 聚合

csharp 复制代码
var sales = new List<decimal> { 100.5m, 200.3m, 150.7m, 300.2m };

int count = sales.Count();           // 4
decimal total = sales.Sum();         // 751.7m
decimal avg = sales.Average();       // 187.925m
decimal max = sales.Max();           // 300.2m
decimal min = sales.Min();           // 100.5m

// 自定义聚合
decimal result = sales.Aggregate(0m, (acc, cur) => acc + cur * 0.1m);

4.10 转换运算符

方法 说明
ToList() 转换为List
ToArray() 转换为数组
ToDictionary() 转换为字典
ToLookup() 转换为Lookup
Cast<T>() 强制转换为泛型集合
OfType<T>() 筛选并转换为指定类型
csharp 复制代码
// 转换示例
var list = students.ToList();
var array = students.ToArray();
var dict = students.ToDictionary(s => s.Id);

五、总结

本文系统梳理了C#进阶编程的四大核心模块:

模块 核心要点
网络编程 Socket通信、TCP/UDP协议、IPAddress/端口、TcpClient/TcpListener
委托与事件 委托作为方法容器、多播委托、Action/Func、事件发布订阅
异步编程 Task创建与启动、async/await、取消任务、UI线程安全更新
LINQ 查询语法与方法语法、标准查询运算符、延迟执行与立即执行

掌握这些知识点,是C#开发者从入门到进阶的关键一步。在实际项目中,这些技术往往相互配合使用------例如使用异步Task进行网络请求,通过委托/事件回调通知UI更新,再用LINQ对数据进行查询处理。


相关推荐
fl1768314 小时前
基于C#WPF实现的内存加速球清理类似360加速球内存清理
开发语言·c#·wpf
CoderIsArt6 小时前
OPC UA 完整介绍、官网、C# 简单使用
开发语言·c#
界面开发小八哥6 小时前
界面控件DevExpress XAF v26.1新版亮点——AI Agent Skills
ai·c#·跨平台·devexpress·ui开发·xaf
csdn_aspnet6 小时前
C# 高效便利的处理数据
开发语言·windows·c#
淡海水7 小时前
03-02-线性-List-T-动态数组布局-扩容与操作成本
数据结构·windows·c#·list·编译·clr·机器码
csdn_aspnet17 小时前
C# 在逗号分割的字符串中判断是否包含指定字符
c#·正则·linq·string·regex·contains
宝桥南山17 小时前
Blazor Web Assembly - 体验一下Authentication State从Server共享给Client
microsoft·微软·c#·asp.net·.net·.netcore
淡海水19 小时前
02-06-历史-托管GC机制-垃圾回收如何影响数据结构选择
c#·编译·gc·机器码
李高钢1 天前
除了 IDE,C#/WPF 开发者每天离不开的 10 个工具
ide·c#·wpf