C#回调函数深度解析
文章目录
- 前言
- 什么是回调函数
- C#中实现回调的方式
- 委托(Delegate)
- 事件(Event)
- Action和Func
- Predicate
- AsyncCallback
- 匿名方法和Lambda表达式
- 回调函数实际应用场景
- 异步编程
- 事件处理
- 策略模式
- LINQ查询
- 回调函数的优缺点
- 优点
- 缺点
- 最佳实践与注意事项
- 总结
- 相关资源
前言
在现代软件开发中,回调函数是一种强大且常用的编程模式,它允许我们将函数作为参数传递给其他函数,从而实现灵活的代码结构和控制流程。C#作为一门功能丰富的面向对象编程语言,提供了多种实现回调的方式,使开发者能够编写更加灵活、可维护的代码。
本文将深入剖析C#中回调函数的概念、实现方式、应用场景以及最佳实践,帮助读者全面理解并有效应用这一重要的编程技术。
什么是回调函数
回调函数本质上是一种编程模式,它允许将一个函数作为参数传递给另一个函数,并在特定事件发生或条件满足时被调用。简单来说,回调函数就是"你调用我,我再回过头来调用你"的一种机制。
在C#中,回调函数通常通过委托(Delegate)、事件(Event)、Lambda表达式等方式实现。它们使代码更具灵活性,能够实现控制反转(IoC),将"如何做"的逻辑与"何时做"的逻辑分离。
C#中实现回调的方式
委托(Delegate)
委托是C#中实现回调函数的基础机制,它是一种类型安全的函数指针,可以引用方法。委托定义了方法的签名,包括返回类型和参数列表。
// 定义一个委托类型,指定回调函数的签名
public delegate void ProcessDataCallback(int result);// 使用委托作为参数的方法
public void ProcessData(int data, ProcessDataCallback callback)
{// 处理数据int result = data * 2;// 处理完成后调用回调函数callback(result);
}// 回调函数的实现
public void HandleResult(int result)
{Console.WriteLine($"处理结果: {result}");
}// 使用示例
public void DelegateExample()
{// 创建委托实例并传递ProcessDataCallback callbackHandler = HandleResult;ProcessData(10, callbackHandler);// 或者使用方法组转换语法ProcessData(20, HandleResult);
}
委托的特点是:
- 类型安全 - 编译器会检查委托与方法签名是否匹配
- 多播 - 可以通过
+=
和-=
操作符将多个方法添加到一个委托实例中 - 封装 - 调用者不需要了解被调用方法的实现细节
事件(Event)
事件是基于委托的一种特殊成员,它提供了一种发布-订阅模型,用于在对象状态改变时通知其他对象。事件是委托的一种受限形式,只能由声明它的类触发,而不能被外部直接调用。
// 定义委托类型
public delegate void StatusChangedEventHandler(object sender, StatusChangedEventArgs e);// 定义事件参数类
public class StatusChangedEventArgs : EventArgs
{public string NewStatus { get; }public StatusChangedEventArgs(string newStatus){NewStatus = newStatus;}
}// 包含事件的类
public class StatusMonitor
{// 声明事件public event StatusChangedEventHandler StatusChanged;private string _status;// 触发事件的方法protected virtual void OnStatusChanged(StatusChangedEventArgs e){// 检查是否有订阅者StatusChanged?.Invoke(this, e);}// 改变状态并触发事件public void ChangeStatus(string newStatus){if (_status != newStatus){_status = newStatus;OnStatusChanged(new StatusChangedEventArgs(newStatus));}}
}// 使用事件的示例
public void EventExample()
{var monitor = new StatusMonitor();// 订阅事件monitor.StatusChanged += OnStatusChanged;// 改变状态,将触发事件monitor.ChangeStatus("运行中");monitor.ChangeStatus("已暂停");// 取消订阅monitor.StatusChanged -= OnStatusChanged;
}// 事件处理方法
private void OnStatusChanged(object sender, StatusChangedEventArgs e)
{Console.WriteLine($"状态已更改为: {e.NewStatus}");
}
事件的特点是:
- 封装性更强 - 只能由声明类触发,外部只能订阅或取消订阅
- 标准化 - 通常遵循.NET事件模式,包含sender和eventArgs参数
- 适合观察者模式 - 一个事件可以有多个订阅者
Action和Func
从.NET 3.5开始,C#引入了Action和Func等通用委托类型,简化了委托的声明和使用。
- Action: 表示无返回值的方法
- Func: 表示有返回值的方法
// 使用Action作为无返回值回调
public void ProcessWithAction(int data, Action<int> callback)
{int result = data * 2;callback(result); // 执行回调
}// 使用Func作为有返回值回调
public void ProcessWithFunc(int data, Func<int, string> callback)
{int result = data * 2;string message = callback(result); // 执行回调并获取返回值Console.WriteLine(message);
}// 使用示例
public void ActionFuncExample()
{// 使用ActionProcessWithAction(10, result => {Console.WriteLine($"Action回调结果: {result}");});// 使用FuncProcessWithFunc(20, result => {return $"Func回调结果: {result}";});
}
Action和Func的优势:
- 预定义 - 不需要自定义委托类型
- 泛型 - 支持不同类型的参数和返回值
- 简洁 - 与Lambda表达式结合使用更加简洁
Predicate
Predicate是一个特殊的委托类型,表示接受一个参数并返回bool值的方法。它通常用于集合筛选、条件判断等场景。
// 使用Predicate筛选集合
public List<int> FilterNumbers(List<int> numbers, Predicate<int> filter)
{List<int> result = new List<int>();foreach (var number in numbers){if (filter(number)) // 调用回调函数进行判断{result.Add(number);}}return result;
}// 使用示例
public void PredicateExample()
{List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };// 筛选偶数List<int> evenNumbers = FilterNumbers(numbers, n => n % 2 == 0);Console.WriteLine(string.Join(", ", evenNumbers)); // 输出: 2, 4, 6, 8, 10// 筛选大于5的数List<int> largeNumbers = FilterNumbers(numbers, n => n > 5);Console.WriteLine(string.Join(", ", largeNumbers)); // 输出: 6, 7, 8, 9, 10
}
AsyncCallback
在异步编程模型(APM)中,AsyncCallback委托用于在异步操作完成时提供回调。虽然现代C#开发更多使用async/await模式,但了解AsyncCallback仍然有助于理解异步编程的发展历程。
// 使用异步回调的示例
public void AsyncCallbackExample()
{// 开始异步操作,并传递回调函数FileStream fs = new FileStream("test.txt", FileMode.Open, FileAccess.Read);byte[] buffer = new byte[1024];// 异步读取并指定回调IAsyncResult asyncResult = fs.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(ReadCompleted), // 回调函数new Tuple<FileStream, byte[]>(fs, buffer) // 状态对象);// 主线程可以继续其他工作...Console.WriteLine("异步读取已开始...");
}// 异步操作完成时的回调函数
private void ReadCompleted(IAsyncResult ar)
{// 从状态对象获取上下文信息var state = (Tuple<FileStream, byte[]>)ar.AsyncState;FileStream fs = state.Item1;byte[] buffer = state.Item2;// 完成异步读取int bytesRead = fs.EndRead(ar);Console.WriteLine($"异步读取完成,读取了 {bytesRead} 字节");fs.Close();// 处理读取的数据string content = Encoding.UTF8.GetString(buffer, 0, bytesRead);Console.WriteLine($"文件内容: {content}");
}
匿名方法和Lambda表达式
匿名方法和Lambda表达式提供了定义内联回调函数的简洁方式,无需单独声明方法。
// 使用匿名方法和Lambda表达式的示例
public void AnonymousAndLambdaExample()
{List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };// 使用匿名方法numbers.ForEach(delegate(int num) {Console.WriteLine($"匿名方法处理: {num * 2}");});// 使用Lambda表达式 - 更简洁numbers.ForEach(num => Console.WriteLine($"Lambda处理: {num * 2}"));// 带有多行逻辑的Lambdanumbers.ForEach(num => {int result = num * num;Console.WriteLine($"数字{num}的平方是{result}");});
}
Lambda表达式的优势:
- 简洁 - 语法更加紧凑
- 闭包 - 可以捕获外部变量
- 表达性 - 代码意图更加清晰
回调函数实际应用场景
异步编程
回调函数在异步编程中扮演着重要角色,允许在操作完成时通知调用者。
// 基于回调的异步操作示例
public void DownloadDataAsync(string url, Action<string> onSuccess, Action<Exception> onError)
{// 启动新线程执行下载操作Task.Run(() =>{try{// 模拟网络请求Console.WriteLine($"开始从{url}下载数据...");Thread.Sleep(2000); // 模拟网络延迟// 下载成功,调用成功回调string data = $"来自{url}的数据";onSuccess(data);}catch (Exception ex){// 发生错误,调用错误回调onError(ex);}});
}// 使用示例
public void AsyncExample()
{Console.WriteLine("开始异步操作...");DownloadDataAsync("https://example.com/api/data",// 成功回调data => {Console.WriteLine($"下载成功: {data}");},// 错误回调error => {Console.WriteLine($"下载失败: {error.Message}");});Console.WriteLine("异步操作已启动,主线程继续执行...");
}
当然,在现代C#中,我们更倾向于使用async/await模式来简化异步代码,但回调模式仍然在某些场景下使用,特别是在与老代码集成或实现特定模式时。
事件处理
GUI应用程序中的事件处理是回调函数的典型应用场景。
// WinForms或WPF中的事件处理示例
public class SimpleForm : Form
{private Button submitButton;private TextBox nameTextBox;public SimpleForm(){// 初始化控件nameTextBox = new TextBox{Location = new Point(70, 30),Size = new Size(200, 20)};submitButton = new Button{Text = "提交",Location = new Point(100, 70)};// 注册事件处理程序(回调函数)submitButton.Click += OnSubmitButtonClick;// 添加控件到窗体Controls.Add(nameTextBox);Controls.Add(submitButton);Text = "回调函数示例";Size = new Size(350, 150);}// 事件处理方法(回调函数)private void OnSubmitButtonClick(object sender, EventArgs e){if (string.IsNullOrWhiteSpace(nameTextBox.Text)){MessageBox.Show("请输入名称");}else{MessageBox.Show($"你好, {nameTextBox.Text}!");nameTextBox.Clear();}}
}
策略模式
回调函数可用于实现策略模式,允许在运行时选择算法的实现。
// 使用回调实现策略模式
public class PaymentProcessor
{// 处理支付的方法,接受支付策略作为回调public void ProcessPayment(decimal amount, Func<decimal, bool> paymentStrategy){Console.WriteLine($"开始处理{amount}元的支付...");// 执行支付策略bool success = paymentStrategy(amount);if (success){Console.WriteLine("支付成功!");}else{Console.WriteLine("支付失败!");}}
}// 使用示例
public void StrategyPatternExample()
{var processor = new PaymentProcessor();// 支付宝支付策略Func<decimal, bool> alipayStrategy = amount => {Console.WriteLine($"使用支付宝支付{amount}元");// 实际支付逻辑...return true; // 假设支付成功};// 微信支付策略Func<decimal, bool> wechatPayStrategy = amount => {Console.WriteLine($"使用微信支付{amount}元");// 实际支付逻辑...return true; // 假设支付成功};// 银行卡支付策略Func<decimal, bool> bankCardStrategy = amount => {Console.WriteLine($"使用银行卡支付{amount}元");// 实际支付逻辑...return false; // 假设支付失败};// 根据不同情况选择不同支付策略processor.ProcessPayment(100.50m, alipayStrategy);processor.ProcessPayment(200.75m, wechatPayStrategy);processor.ProcessPayment(500.00m, bankCardStrategy);
}
LINQ查询
LINQ查询广泛使用委托和Lambda表达式作为回调函数,实现数据筛选、转换和聚合等操作。
// LINQ中的回调函数示例
public class Product
{public int Id { get; set; }public string Name { get; set; }public decimal Price { get; set; }public string Category { get; set; }
}public void LinqExample()
{List<Product> products = new List<Product>{new Product { Id = 1, Name = "笔记本电脑", Price = 6999, Category = "电子产品" },new Product { Id = 2, Name = "手机", Price = 2999, Category = "电子产品" },new Product { Id = 3, Name = "耳机", Price = 299, Category = "配件" },new Product { Id = 4, Name = "键盘", Price = 199, Category = "配件" },new Product { Id = 5, Name = "鼠标", Price = 99, Category = "配件" }};// Where方法接受Predicate作为回调函数var expensiveProducts = products.Where(p => p.Price > 1000);// Select方法接受Func作为回调函数进行转换var productNames = products.Select(p => p.Name);// OrderBy方法接受Func作为回调函数确定排序键var orderedProducts = products.OrderBy(p => p.Price);// GroupBy方法接受Func作为回调函数确定分组键var productsByCategory = products.GroupBy(p => p.Category);// ForEach方法接受Action作为回调函数Console.WriteLine("价格超过1000元的产品:");expensiveProducts.ToList().ForEach(p => {Console.WriteLine($"{p.Name} - {p.Price}元");});// 组合使用多个回调函数var result = products.Where(p => p.Category == "电子产品").OrderByDescending(p => p.Price).Select(p => new { p.Name, p.Price });Console.WriteLine("\n电子产品(按价格降序):");foreach (var item in result){Console.WriteLine($"{item.Name} - {item.Price}元");}
}
回调函数的优缺点
优点
- 灵活性: 允许在运行时决定要执行的代码,实现动态行为。
- 松耦合: 减少组件之间的依赖,提高代码的可维护性。
- 可扩展性: 可以在不修改原有代码的情况下,通过回调添加新功能。
- 控制反转: 框架控制程序流程,开发者只需提供特定逻辑。
- 可组合性: 多个回调函数可以组合使用,实现复杂功能。
缺点
- 回调地狱: 多层嵌套回调可能导致代码难以理解和维护。
- 调试困难: 回调函数的执行流程可能不连续,增加调试难度。
- 线程安全问题: 在多线程环境中,回调可能引发同步问题。
- 可读性降低: 过度使用回调可能使代码逻辑分散,降低可读性。
- 上下文传递: 需要额外机制传递上下文信息,增加复杂性。
最佳实践与注意事项
-
使用现代语法
- 优先使用Action/Func而非自定义委托
- 使用Lambda表达式简化回调定义
- 考虑使用async/await替代传统回调
-
错误处理
- 在回调中始终包含异常处理
- 考虑使用try/catch/finally保护回调执行
- 提供错误回调选项
public void SafeCallbackExample(Action callback) {try{callback?.Invoke();}catch (Exception ex){Console.WriteLine($"回调执行错误: {ex.Message}");} }
-
避免回调地狱
- 分解复杂回调链
- 使用命名函数提高可读性
- 考虑使用任务(Task)或异步方法
-
避免内存泄漏
- 注意回调持有的对象引用
- 适当使用弱引用
- 记得取消不再需要的事件订阅
-
保持线程安全
- 使用线程安全集合存储回调
- 注意多线程环境中的同步问题
- 考虑使用线程同步机制
public class ThreadSafeCallbackManager {private readonly List<Action> _callbacks = new List<Action>();private readonly object _lock = new object();public void AddCallback(Action callback){lock (_lock){_callbacks.Add(callback);}}public void ExecuteAll(){List<Action> callbacksCopy;lock (_lock){callbacksCopy = _callbacks.ToList();}foreach (var callback in callbacksCopy){try{callback();}catch (Exception ex){Console.WriteLine($"执行回调时出错: {ex.Message}");}}} }
总结
回调函数是C#中一种强大的编程模式,提供了灵活、松耦合的代码组织方式。通过委托、事件、Lambda表达式等机制,C#为开发者提供了多种实现回调的选择。
回调函数广泛应用于异步编程、事件处理、策略模式、LINQ查询等场景,能够显著提高代码的灵活性和可维护性。然而,使用回调也需要注意潜在的回调地狱、线程安全和内存管理等问题。
随着C#语言的发展,回调函数的实现方式也在不断演进,从传统的委托到现代的Lambda表达式和异步编程模型,为开发者提供了更加简洁、高效的编程体验。掌握回调函数的使用,是成为高级C#开发者的重要一步。
相关资源
- Microsoft Docs: 委托和事件
- Microsoft Docs: Lambda表达式
- Microsoft Docs: 异步编程模式
相关文章:

C#回调函数深度解析
文章目录 前言什么是回调函数C#中实现回调的方式委托(Delegate)事件(Event)Action和FuncPredicateAsyncCallback匿名方法和Lambda表达式 回调函数实际应用场景异步编程事件处理策略模式LINQ查询 回调函数的优缺点优点缺点 最佳实践与注意事项总结相关资源 前言 在现代软件开发…...

通义智文开源QwenLong-L1: 迈向长上下文大推理模型的强化学习
🎉 动态 2025年5月26日: 🔥 我们正式发布🤗QwenLong-L1-32B——首个采用强化学习训练、专攻长文本推理的LRM模型。在七项长文本文档问答基准测试中,QwenLong-L1-32B性能超越OpenAI-o3-mini和Qwen3-235B-A22B等旗舰LRM,…...
HTTP代理的实际用处有哪些?HTTP代理强在哪里?
嘿,小伙伴们!今天咱们来聊聊 HTTP 代理。大家可能会经常在网页上看到类似“HTTP 代理”的关键词,是不是会心生疑惑:这不就是个技术名词吗?它到底有啥用呢?别急,听我娓娓道来。 一、什么是HTTP代…...

低代码——表单生成器以form-generator为例
主要执行流程说明: 初始化阶段 : 接收表单配置对象formConf深拷贝配置,初始化表单数据和验证规则处理每个表单组件的默认值和特殊配置(如文件上传) 渲染阶段 : 通过render函数创建el-form根组件递归渲染表…...
【Vue Vapor Mode :技术突破与性能优化的可能性】
Vue Vapor Mode :技术突破与性能优化的可能性 前言 作为一名有着Vue 2经验和Vue 3经验的开发者,你一定深刻体会过Vue从Options API到Composition API的演进,也感受过Vue 3在性能上相比Vue 2的显著提升。现在,Vue团队正在开发一个…...
Parasoft C++Test软件单元测试_常见问题及处理
系列文章目录 Parasoft C++Test软件静态分析:操作指南(编码规范、质量度量)、常见问题及处理 Parasoft C++Test软件单元测试:操作指南、实例讲解、常见问题及处理 Parasoft C++Test软件集成测试:操作指南、实例讲解、常见问题及处理 进阶扩展:自动生成静态分析文档、自动…...
vue渲染数组各子项实现文本超出宽度显示tooltip
vue渲染数组各子项实现文本超出宽度显示tooltip 需求背景 vue项目中,通过v-for渲染数组,子项中均存在一行描述文字。当描述文字超出固定宽度时,显示省略号并且鼠标悬浮时显示tooltip;当描述文字不超出固定宽度时则正常显示&…...
libreoffice容器word转pdf
先说结论,市面上不花钱的,简单的效果好的就是这个种方式,在线测试下来不如命令转的效果好。AsposeWords和SpireDoc效果都不错,但是只有这个word转pdf感觉花3-5w不划算。 下载容器路径 https://docker.aityp.com/i/search?searchl…...
AI模型升级与机器人产业落地同步推进
2025年5月28日,中国AI领域迎来两项实质性进展。DeepSeek-R1模型完成小版本试升级,开源社区发布其0528版本。本次更新聚焦语义理解精准性、复杂逻辑推理和长文本处理能力的提升。在代码测试平台Live CodeBench中,开发者反馈其编程性能已接近Op…...
安全编码与AI接口权限控制
安全编码与AI接口权限控制 在AI系统中,模型服务的开放接口往往涉及敏感数据、核心算法与算力资源,如果缺乏有效的安全编码与权限控制机制,极易引发数据泄露、滥用调用或非法操作等问题。本节将从“接口安全策略”“权限验证流程”“Token管控机制”“多租户身份隔离”四个方…...

linux centos 服务器性能排查 vmstat、top等常用指令
背景:项目上经常出现系统运行缓慢,由于数据库服务器是linux服务器,记录下linux服务器性能排查常用指令 vmstat vmstat介绍 vmstat 命令报告关于内核线程、虚拟内存、磁盘、陷阱和 CPU 活动的统计信息。由 vmstat 命令生成的报告可以用于平衡系统负载活动。系统范围内的这…...
MySQL----视图的创造和使用
这里写目录标题 **创造视图****查看视图****修改视图****更新视图****删除视图** 创造视图 使用create view…创造 语法格式 create[algorithm {undefiend|merge|temptable}]view <视图名> [(<字段名1>[,……,字段名n])]as <select 语…...
c/c++的opencv伽马噪声
理解与实现 C/OpenCV 中的伽马噪声 🖼️ 噪声是大多数图像采集过程中固有的组成部分。理解和模拟不同类型的噪声对于开发鲁棒的图像处理算法至关重要,尤其是在去噪方面。虽然高斯噪声和椒盐噪声是常被讨论的类型,但伽马噪声(通常…...

LiveGBS国标视频平台收流模式:UDP、TCP被动与TCP主动传输模式之差异剖析
LiveGBS国标视频平台收流模式:UDP、TCP被动与TCP主动传输模式之差异剖析 1、背景2、信令传输3、视频流传输3.1、UDP传输模式3.2、TCP被动传输模式3.3、TCP主动传输模式 4、WEB配置流传输模式4.1、编辑模式4.2、下拉切换模式 5、搭建GB28181视频直播平台 1、背景 在…...
跳表(Skip List)查找算法详解
1、原理 跳表是一种概率型数据结构,通过多层有序链表实现高效查找,时间复杂度接近平衡树(O(log n))。其核心思想是通过层级索引加速搜索,结构类似火车时刻表的“快车-慢车”模式。 关键特性: 多层链表&a…...
React从基础入门到高级实战:React 核心技术 - React 与 TypeScript:构建类型安全的应用
React 与 TypeScript:构建类型安全的应用 在现代前端开发中,TypeScript 因其强大的类型系统和编译时错误检查功能,已成为 React 开发者的热门选择。通过为代码添加类型定义,TypeScript 能够显著提升代码的健壮性、可维护性和团队…...
Django orm详解--组成部件
Django ORM 的核心部件可分为模型系统、查询系统、数据库后端和辅助工具四大类,每个部件负责不同的职责,共同实现对象与关系数据库的映射。以下是核心部件的分层解析: 一、模型系统(Model System) 1. 模型基类&#…...

Tomcat 使用与配置全解
一、 Tomcat简介 Tomcat服务器是Apache的一个开源免费的Web容器。它实现了JavaEE平台下部分技术规范,属于轻量级应用服务器。 1. Tomcat版本 Tomcat版本 JDK版本 Servlet版本 JSP版本 10.0.X 8 and later 5.0 3.0 9.0.x 8 and later 4.0 2.3 8.0.x 7…...
Chrome 开发中的任务调度与线程模型实战指南
内容 概述 快速入门指南 核心概念线程词典 线程任务优先使用序列而不是物理线程 发布并行任务 直接发布到线程池通过 TaskRunner 发布 发布顺序任务 发布到新序列发布到当前(虚拟)主题 使用序列代替锁将多个任务发布到同一线程 发布到浏览器进程中的主线…...

aws instance store 的恢复
1: aws instance store 要在launch instance 才可以创建,而且,通过snapshot 恢复后,instance store 里面的数据会丢失。 下面是创建instance store 的过程,和通过两种方式恢复,发现/etc/fstab 不同的写法,有的不能启动: [root@ip-xx ~]# lsblk NAME MAJ:MIN RM …...
从零开始创建 Vue 3 开发环境并构建第一个 Demo
Vue 3 是目前前端开发中非常流行的渐进式 JavaScript 框架。本文将手把手带你完成从环境搭建到运行一个基础 Vue 3 示例的全过程。 📦 一、环境准备 1. 安装 Node.js Vue 项目依赖 Node.js 运行环境,请确保你的电脑已安装 Node.js(建议使用…...

EasyRTC音视频实时通话助力微信小程序:打造低延迟、高可靠的VoIP端到端呼叫解决方案
一、方案概述 在数字化通信浪潮下,端到端实时音视频能力成为刚需。依托庞大用户生态的微信小程序,是实现此类功能的优质载体。基于WebRTC的EasyRTC音视频SDK,为小程序VoIP呼叫提供轻量化解决方案,通过技术优化实现低延迟通信&a…...

STM32 SPI通信(软件)
一、SPI简介 SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slav…...

每日刷题c++
快速幂 #include <iostream> using namespace std; #define int long long int power(int a, int b, int p) {int ans 1;while (b){if (b % 2){ans * a;ans % p; // 随时取模}a * a;a % p; // 随时取模b / 2;}return ans; } signed main() {int a, b, p;cin >> a …...
(自用)Java学习-5.19(地址管理,三级联动,预支付)
1. 地址管理模块 地址展示 前端:通过 showAddress() 发起 Ajax GET 请求,动态渲染地址列表表格,使用 #{tag}、#{name} 等占位符替换真实数据。 后端: 控制器层调用 AddressService,通过 AddressMapper 查询用户地址数…...
【容器】docker使用问题处理
问题一、systemctl start docker启动报 ERROR: ZONE_CONFLICT: docker0 already bound to a zone 处理方法 firewall-cmd --permanent --zonedocker --change-interfacedocker0 systemctl restart firewalld 问题二、启动容器报 ptables failed/iptables: No chain/target/…...

ChemDraw 2023|Win英文|化学结构编辑器|安装教程
软件下载 【名称】:ChemDraw 2023 【大小】:1.34G 【语言】:英文界面 【安装环境】:Win10/Win11 【夸克网盘下载链接】(务必手机注册): https://pan.quark.cn/s/320bcb67da80 【网站下载…...
Vue3实现提示文字组件
Vue3 实现一个文字提示组件(Tooltip) 文字提示(Tooltip)是前端开发中非常常见的组件,通常用于在用户悬停某个元素时显示额外的信息。 一、需求分析 我们要实现一个 Vue3 的文字提示组件,具备以下功能&…...
JAVA与C语言之间的差异(一)
一、代码习惯以及主函数 JAVA中{在使用的时候不要换行 public static void main(String[] args) {int[] array {1, 2, 3};for(int i 0; i < array.length; i){System.out.println(array[i] " ");}} 其次,以main函数为主函数: public …...
深入剖析 C 语言中的指针数组与数组指针
资料合集下载链接: https://pan.quark.cn/s/472bbdfcd014 在C语言中,指针是其强大和灵活性的核心。然而,围绕指针的概念有很多容易混淆的地方,其中“指针数组”和“数组指针”就是一对常见的“双胞胎”概念。它们名称相似,但含义和用法却大相径庭。 本文旨在清…...