当前位置: 首页 > news >正文

字段临时缓存包装器

前言

在实际开发中,我们有时候存在一种需求,例如对于某个字段,我们希望在某个明确的保存节点前对字段的修改都仅作为缓存保留,最终是否应用这些修改取决于某些条件,比如玩家对游戏设置的修改可能需要玩家明确确认应用修改后才会保存下来,在此之前玩家在游戏界面上的所有修改都是临时的。


本文基于这个需求探索出了一种解决方案“字段临时缓存包装器”,通过创建字段或用于存储字段临时数据的数据结构的副本来实现临时缓存,虽然我们同样可以采用直接声明一个副本字段的方式来达到同样的目的,但是这可能会增加冗余代码,且不利于代码的维护,通过包装器来封装临时缓存的通用逻辑,与具体业务逻辑隔离。

代码

v1.0
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Newtonsoft.Json;/// <summary>
/// 临时包装器
/// </summary>
/// <typeparam name="T">字段类型</typeparam>
/// <remarks>
/// 该类主要用于创造某个字段的副本作为该字段的临时缓存,避免直接修改源字段。
/// </remarks>
public class TempWrapper<T> : IDisposable
{/// <summary>/// 是否为值类型/// <para>提示:若为true则表示包装字段为值类型,否则为引用类型</para>/// </summary>public static bool isValueType => _isValueType;/// <summary>/// 缓存字段/// <para>提示:对于值类型而言,该属性涉及拷贝</para>/// </summary>public T value{get{if (_isDisposed) throw new InvalidOperationException("The wrapper is disposed.");return _value;}set{if (_isDisposed) throw new InvalidOperationException("The wrapper is disposed.");_value = value;}}/// <summary>/// 获取引用/// <para>提示:对于值类型而言,该属性直接返回引用从而避免拷贝</para>/// </summary>public ref T refrence{get{if (_isDisposed) throw new InvalidOperationException("The wrapper is disposed.");return ref _value;}}/// <summary>/// 是否已经释放/// </summary>public bool isDisposed => _isDisposed;static readonly bool _isValueType = typeof(T).IsValueType;static readonly bool _isDisposable = typeof(IDisposable).IsAssignableFrom(typeof(T));static readonly object _key = new object();T _value;bool _isDisposed;TempWrapper() { }/// <summary>/// 包装指定字段并返回包装类/// </summary>/// <param name="value">待包装字段的引用</param>/// <remarks>/// <para>提示:采用二进制序列化和反序列化生成字段副本</para>/// <para>提示:该方法仅可用于被 <c>Serializable</c> 标记的字段类型</para>/// </remarks>public static TempWrapper<T> WrapByBinary(ref T value){lock (_key){try{TempWrapper<T> wrapper = new TempWrapper<T>();if (_isValueType) wrapper._value = value;else{using (MemoryStream ms = new MemoryStream()){IFormatter formatter = new BinaryFormatter();formatter.Serialize(ms, value);ms.Seek(0, SeekOrigin.Begin);wrapper._value = (T)formatter.Deserialize(ms);}}return wrapper;}catch (Exception e){throw new InvalidOperationException("Failed to wrap.", e);}}}/// <summary>/// 包装指定字段并返回包装类/// <para>提示:采用JSON序列化和反序列化生成字段副本</para>/// </summary>/// <param name="value">待包装字段的引用</param>public static TempWrapper<T> WrapByJson(ref T value){lock (_key){try{TempWrapper<T> wrapper = new TempWrapper<T>();if (_isValueType) wrapper._value = value;else{string jsonStr = JsonConvert.SerializeObject(value);wrapper._value = JsonConvert.DeserializeObject<T>(jsonStr);}return wrapper;}catch (Exception e){throw new InvalidOperationException("Failed to wrap.", e);}}}/// <summary>/// 包装生成器所生成的字段并返回包装类/// </summary>/// <param name="creator">生成器</param>public static TempWrapper<T> WrapByCustom(Func<T> creator){lock (_key){try{TempWrapper<T> wrapper = new TempWrapper<T>() { _value = creator() };return wrapper;}catch (Exception e){throw new InvalidOperationException("Failed to wrap.", e);}}}/// <summary>/// 解包包装器并赋值给指定的字段/// </summary>/// <remarks>/// <para>提示:采用二进制序列化和反序列化解包</para>/// <para>提示:该方法仅可用于被 <c>Serializable</c> 标记的字段类型</para>/// </remarks>public void UnWrapByBinary(ref T value){if (_isValueType) value = _value;else{lock (_key){using (MemoryStream ms = new MemoryStream()){IFormatter formatter = new BinaryFormatter();formatter.Serialize(ms, _value);ms.Seek(0, SeekOrigin.Begin);value = (T)formatter.Deserialize(ms);}}}}/// <summary>/// 解包包装器并赋值给指定的字段/// <para>提示:采用JSON序列化和反序列化解包</para>/// </summary>public void UnwrapByJson(ref T value){if (_isValueType) value = _value;else{lock (_key){string jsonStr = JsonConvert.SerializeObject(_value);value = JsonConvert.DeserializeObject<T>(jsonStr);}}}/// <summary>/// 释放包装器所包装的字段/// <para>提示:当所包装字段实现了IDisposable接口时该方法才有效</para>/// </summary>public void Dispose(){if (_isDisposed) return;DoDispose(true);GC.SuppressFinalize(this);}void DoDispose(bool disposing){if (_isDisposed) return;_isDisposed = true;if (disposing && _isDisposable && _value is IDisposable ds)ds.Dispose();}~TempWrapper(){DoDispose(false);}
}
v1.1 
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Newtonsoft.Json;/// <summary>
/// 临时包装器
/// </summary>
/// <typeparam name="T">字段类型</typeparam>
/// <remarks>
/// 该类主要用于创造某个字段的副本作为该字段的临时缓存,避免直接修改源字段。
/// </remarks>
public class TempWrapper<T> : IDisposable
{/// <summary>/// 是否为值类型/// <para>提示:若为true则表示包装字段为值类型,否则为引用类型</para>/// </summary>public static bool isValueType => _isValueType;/// <summary>/// 缓存字段/// <para>提示:对于值类型而言,该属性涉及拷贝</para>/// </summary>public T value{get{if (_isDisposed) throw new InvalidOperationException("The wrapper is disposed.");return _value;}set{if (_isDisposed) throw new InvalidOperationException("The wrapper is disposed.");_value = value;}}/// <summary>/// 获取引用/// <para>提示:对于值类型而言,该属性直接返回引用从而避免拷贝</para>/// </summary>public ref T refrence{get{if (_isDisposed) throw new InvalidOperationException("The wrapper is disposed.");return ref _value;}}static readonly bool _isValueType = typeof(T).IsValueType;static readonly bool _isDisposable = typeof(IDisposable).IsAssignableFrom(typeof(T));static readonly object _key = new object();T _value;bool _isDisposed;TempWrapper() { }/// <summary>/// 包装指定字段并返回包装类/// </summary>/// <param name="value">待包装字段的引用</param>/// <remarks>/// <para>提示:采用二进制序列化和反序列化生成字段副本</para>/// <para>提示:该方法仅可用于被 <c>Serializable</c> 标记的字段类型</para>/// </remarks>public static TempWrapper<T> WrapByBinary(ref T value){lock (_key){try{TempWrapper<T> wrapper = new TempWrapper<T>();if (_isValueType) wrapper._value = value;else{using (MemoryStream ms = new MemoryStream()){IFormatter formatter = new BinaryFormatter();formatter.Serialize(ms, value);ms.Seek(0, SeekOrigin.Begin);wrapper._value = (T)formatter.Deserialize(ms);}}return wrapper;}catch (Exception e){throw new InvalidOperationException("Failed to wrap.", e);}}}/// <summary>/// 包装指定字段并返回包装类/// <para>提示:采用JSON序列化和反序列化生成字段副本</para>/// </summary>/// <param name="value">待包装字段的引用</param>public static TempWrapper<T> WrapByJson(ref T value){lock (_key){try{TempWrapper<T> wrapper = new TempWrapper<T>();if (_isValueType) wrapper._value = value;else{string jsonStr = JsonConvert.SerializeObject(value);wrapper._value = JsonConvert.DeserializeObject<T>(jsonStr);}return wrapper;}catch (Exception e){throw new InvalidOperationException("Failed to wrap.", e);}}}/// <summary>/// 包装生成器所生成的字段并返回包装类/// </summary>/// <param name="creator">生成器</param>public static TempWrapper<T> WrapByCustom(Func<T> creator){lock (_key){try{TempWrapper<T> wrapper = new TempWrapper<T>() { _value = creator() };return wrapper;}catch (Exception e){throw new InvalidOperationException("Failed to wrap.", e);}}}/// <summary>/// 解包包装器并赋值给指定的字段/// </summary>/// <remarks>/// <para>提示:采用二进制序列化和反序列化解包</para>/// <para>提示:该方法仅可用于被 <c>Serializable</c> 标记的字段类型</para>/// </remarks>public void UnWrapByBinary(ref T value){if (_isDisposed) throw new InvalidOperationException("The wrapper is disposed.");if (_isValueType) value = _value;else{using (MemoryStream ms = new MemoryStream()){IFormatter formatter = new BinaryFormatter();formatter.Serialize(ms, _value);ms.Seek(0, SeekOrigin.Begin);value = (T)formatter.Deserialize(ms);}}}/// <summary>/// 解包包装器并赋值给指定的字段/// <para>提示:采用JSON序列化和反序列化解包</para>/// </summary>public void UnwrapByJson(ref T value){if (_isDisposed) throw new InvalidOperationException("The wrapper is disposed.");if (_isValueType) value = _value;else{string jsonStr = JsonConvert.SerializeObject(_value);value = JsonConvert.DeserializeObject<T>(jsonStr);}}/// <summary>/// 释放包装器所包装的字段/// <para>提示:当所包装字段实现了IDisposable接口时该方法才有效</para>/// </summary>public void Dispose(){if (_isDisposed) throw new InvalidOperationException("The wrapper is disposed.");DoDispose(true);GC.SuppressFinalize(this);}void DoDispose(bool disposing){if (_isDisposed) return;_isDisposed = true;if (disposing && _isDisposable && _value is IDisposable ds)ds.Dispose();}~TempWrapper(){DoDispose(false);}
}

测试

#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;// TempWrapper测试脚本
public class TempWrapperTest : MonoBehaviour
{[SerializeField] int[] intArray;[SerializeField] string[] strArray;[SerializeField] StructA[] structaArray;[SerializeField] ClassA classA;[SerializeField] StructA structA;[SerializeField] Transform tf;[Serializable]struct StructA{public string key;public int value;public override string ToString(){return $"(key:{key},value:{value})";}}[Serializable]class ClassA{public string key;public StructA structA;public override string ToString(){StringBuilder builder = new StringBuilder("[key:");builder.Append(key).Append(",").Append($"StructA:{structA}");builder.Append("]");return builder.ToString();}}TempWrapper<int[]> intArrayWrapper;TempWrapper<string[]> strArrayWrapper;TempWrapper<StructA[]> structaArrayWrapper;TempWrapper<ClassA> classaWrapper;TempWrapper<StructA> structaWrapper;TempWrapper<Transform> tfWrapper;void Awake(){if (Application.isPlaying){intArrayWrapper = TempWrapper<int[]>.WrapByBinary(ref intArray);strArrayWrapper = TempWrapper<string[]>.WrapByJson(ref strArray);structaArrayWrapper = TempWrapper<StructA[]>.WrapByBinary(ref structaArray);classaWrapper = TempWrapper<ClassA>.WrapByBinary(ref classA);structaWrapper = TempWrapper<StructA>.WrapByBinary(ref structA);tfWrapper = TempWrapper<Transform>.WrapByCustom(() => Instantiate(tf));Instantiate(transform);}}void OnDestroy(){intArrayWrapper.Dispose();strArrayWrapper.Dispose();structaArrayWrapper.Dispose();classaWrapper.Dispose();structaWrapper.Dispose();tfWrapper.Dispose();}void Update(){if (Input.GetKeyDown(KeyCode.Q)){PrintWrapper();PrintHashCode();PrintWrapperHashCode();}if (Input.GetKeyDown(KeyCode.W)){WriteWrapper();UnWrap();}}void PrintWrapper(){intArrayWrapper.value.LogC("IntArrayWrapper:");strArrayWrapper.value.LogC("StrArrayWrapper:");structaArrayWrapper.value.LogC(s => $"[key:{s.key},value:{s.value}]", "StructaArrayWrapper:");LogUtility.Log("ClassAWrapper:" + classaWrapper.value);LogUtility.Log("StructAWrapper:" + structaWrapper.value);LogUtility.Log("TfWrapper:" + tfWrapper.value.position);}void PrintHashCode(){LogUtility.Log("IntArray:" + intArray.GetHashCode());LogUtility.Log("StrArray:" + strArray.GetHashCode());LogUtility.Log("StructaArray:" + structaArray.GetHashCode());LogUtility.Log("ClassA:" + classA.GetHashCode());LogUtility.Log("StructA:" + structA.GetHashCode());LogUtility.Log("Tf:" + tf.GetHashCode());}void PrintWrapperHashCode(){LogUtility.Log("IntArrayWrapper:" + intArrayWrapper.value.GetHashCode());LogUtility.Log("StrArrayWrapper:" + strArrayWrapper.value.GetHashCode());LogUtility.Log("StructaArrayWrapper:" + structaArrayWrapper.value.GetHashCode());LogUtility.Log("ClassAWrapper:" + classaWrapper.value.GetHashCode());LogUtility.Log("StructAWrapper:" + structaWrapper.value.GetHashCode());LogUtility.Log("TfWrapper:" + tfWrapper.value.GetHashCode());}void WriteWrapper(){List<int> ints = new List<int>(intArrayWrapper.value) { 99, 100 };intArrayWrapper.value = ints.ToArray();List<string> strs = new List<string>(strArrayWrapper.value) { "D", "E" };strArrayWrapper.value = strs.ToArray();List<StructA> strcutAs = new List<StructA>(structaArrayWrapper.value){new StructA { key = "D", value = 99 },new StructA { key = "E", value = 100 }};structaArrayWrapper.value = strcutAs.ToArray();structaWrapper.refrence.key = "E";structaWrapper.refrence.value = 1000;classaWrapper.value.key = "DE";classaWrapper.value.structA.key = "D";classaWrapper.value.structA.value = 999;tfWrapper.value.position = Vector3.zero;}void UnWrap(){intArrayWrapper.UnWrapByBinary(ref intArray);strArrayWrapper.UnwrapByJson(ref strArray);structaArrayWrapper.UnWrapByBinary(ref structaArray);structaWrapper.UnWrapByBinary(ref structA);classaWrapper.UnWrapByBinary(ref classA);}
}
#endif
v1.0 
用例ID用例名称前者测试预期结果是否通过
1简单值类型数组可缓存通过
2不可变引用类型数组可缓存通过
3复合值类型数组可缓存通过
4自定义引用类型可缓存通过
5自定义值类型可缓存通过
6Unity对象可缓存通过
v1.1
用例ID用例名称前者测试预期结果是否通过
1简单值类型数组可缓存通过
2不可变引用类型数组可缓存通过
3复合值类型数组可缓存通过
4自定义引用类型可缓存通过
5自定义值类型可缓存通过
6Unity对象可缓存通过

分析

字段临时缓存包装器有三种包装字段的方式,分别是WrapByBinary、WrapByJson和WrapByCustom,三种方式各有优缺点,择优而用。WrapByBinary采用二进制序列化和反序列化生成字段副本,该方法仅可用于被 Serializable 标记的字段类型。WrapByJson采用JSON序列化和反序列化生成字段副本,它虽然比前者包装范围更广,但是不可避免可能会依赖第三方用于JSON序列化和反序列化的库。WrapByCustom则是对前两种方式的补充,当前两种方式都不适用时,则可以自定义包装方式,例如对于Unity对象来说,需要通过Instantiate方法创建对象副本,这个时候就只能用自定义的方法进行包装。


返回的包装器提供了一些属性和方法,可用于判断是否为值类型、缓存的字段和缓存字段的引用(值类型),提供了针对WrapByBinary和WrapByJson包装方法的解包方法,还提供了显式释放包装器的方法。对于包装值类型时,我们可以通过获取缓存字段的引用来避免拷贝,解包方法用于将临时缓存的数据重新写入被包装字段中。通过显式释放包装器可以保证那些使用了非托管资源的类型(实现了IDisposable接口)进行资源的释放工作,从而避免内存泄漏等问题。


但是该包装器存在一些不可避免的限制,若所包装字段越复杂,其性能损耗越高,这是不可避免的。而对于复杂类型,建议自定义一个数据结构作为临时缓存的包装类型。


注意:不要使用包装器去包装其声明所在的类,特别是对于自定义包装逻辑要避免无限递归,否则会导致栈溢出或内存泄漏问题。示例代码如下:

// 该方法将导致栈溢出
public class A
{TempWrapper<A> wrapper;public A(){wrapper = TempWrapper<A>.WrapByCustom(() => new A());}
}// 该方法将导致内存泄漏
public class B:Monobehaviour
{TempWrapper<Transform> wrapper;void Awake(){// Instantiate方法去克隆当前组件对象的Transform组件就会导致无限递归wrapper = TempWrapper<Transform>.WrapByCustom(() => Instantiate(transform));}
}

版本改进

版本号改进内容
v1.1

1.实例方法不使用线程锁,仅对静态方法使用线程锁;

2.手动执行Dispose方法释放包装器后,所有对公开实例成员的访问都将触发异常;

3.删除IsDisposed属性;

............

系列文章

......

如果这篇文章对你有帮助,请给作者点个赞吧!

相关文章:

字段临时缓存包装器

前言 在实际开发中&#xff0c;我们有时候存在一种需求&#xff0c;例如对于某个字段&#xff0c;我们希望在某个明确的保存节点前对字段的修改都仅作为缓存保留&#xff0c;最终是否应用这些修改取决于某些条件&#xff0c;比如玩家对游戏设置的修改可能需要玩家明确确认应用修…...

Python(三)——列表

文章目录 创建列表访问下标遍历列表元素新增元素查找元素删除元素连接列表切片操作 创建列表 创建列表主要有两种方式 [ ]表示一个空的列表 a [] print(type(a)) # <class list> print(a) # []通过list()的方式来创建一个空列表 a list() print(type(a)) # …...

MySQL--三大范式(超详解)

目录 一、前言二、三大范式2.1概念2.2第一范式&#xff08;1NF&#xff09;2.3第二范式&#xff08;2NF&#xff09;2.3第三范式&#xff08;3NF&#xff09; 一、前言 欢迎大家来到权权的博客~欢迎大家对我的博客进行指导&#xff0c;有什么不对的地方&#xff0c;我会及时改进…...

追梦无Bug的软件世界

追梦无Bug的软件世界&#xff1a;测试人员的视角与探索 我有一个梦想&#xff0c;今天我们共同承载着一个愿景&#xff1a;创造一个没有Bug的软件世界。 我梦想有一天&#xff0c;用户将享受到完全无Bug的软件体验&#xff0c;用户不再因为软件中的Bug而感到困扰和沮丧。 我梦…...

在C#中使用Redis实现高效消息队列

使用Redis实现C#中的消息队列 Redis是一种开源的内存数据结构存储系统,因其高性能和灵活性被广泛用于缓存、数据库和消息队列等场景。本文将详细介绍如何在C#中使用Redis实现一个简单的消息队列,涵盖环境准备、代码实现和使用示例。 1. 环境准备 1.1 安装Redis 首先,确保…...

微服务JMeter解析部署使用全流程

目录 1、介绍 2、下载 3、运行 4、设置简体中文版 5、开始测试 1、添加线程组 2、添加监听器 3、添加请求 先.测试userController里的查询方法 6、查看结果 1、查看结果树 2、汇总报告 3、聚合报告 7、JMeter报错 1、介绍 Apache JMeter 是 Apache 组织基于 Java…...

Python 从入门到实战32(数据库MySQL)

我们的目标是&#xff1a;通过这一套资料学习下来&#xff0c;通过熟练掌握python基础&#xff0c;然后结合经典实例、实践相结合&#xff0c;使我们完全掌握python&#xff0c;并做到独立完成项目开发的能力。 上篇文章我们讨论了数据库编程接口操作的相关知识。今天我们将学习…...

hrnet训练的pt模型结合目标检测进行关键点识别的更准确前向推理

本篇在将图像输入hrnet识别之前先进行目标检测来确定识别的位置&#xff0c;让识别更加精准。 本段代码设置了一个区域框BOX&#xff0c;让人走入区域内才开始检测&#xff0c;适用于考核等场景&#xff0c;也可以直接去掉BOX也是一样的效果。若画面背景中有多个行人&#xff0…...

Leetcode 3306. Count of Substrings Containing Every Vowel and K Consonants II

Leetcode 3306. Count of Substrings Containing Every Vowel and K Consonants II 1. 解题思路2. 代码实现 题目链接&#xff1a;3306. Count of Substrings Containing Every Vowel and K Consonants II 1. 解题思路 这一题的话思路上就是一个滑动窗口&#xff0c;考察没一…...

算法笔记(五)——分治

文章目录 算法笔记&#xff08;五&#xff09;——分治快排颜色分类排序数组数组中的第K个最大元素库存管理 III 归并排序数组交易逆序对的总数计算右侧小于当前元素的个数翻转对 算法笔记&#xff08;五&#xff09;——分治 分治算法字面上的解释是“分而治之”&#xff0c;就…...

多级侧边菜单(递归)

需要编写两个文件 aside-menu.vue 和 menu-item.vue menu-item.vue <script setup> defineOptions({name: MenuItem}) defineProps({menuList: Array}) </script><template><template v-for"menu of menuList"><!-- 如果当前有子菜单&a…...

JavaScript break与continue语句

break语句和continue语句都具有跳转作用&#xff0c;可以让代码不按既有的顺序执行。 break break语句用于跳出代码块或循环 for(i0;i<100;i){if(i5){break;}console.log(i);} continue continue语句用于应即终止本轮循环,返回循环结构的头部&#xff0c;开始下一轮循环。…...

算法【从递归入手一维动态规划】

动态规划&#xff1a;用空间代替重复计算&#xff0c;包含一整套原理和技巧的总和。后面会有非常多的文章介绍动态规划。 有些递归在展开计算时&#xff0c;总是重复调用同一个子问题的解&#xff0c;这种重复调用的递归变成动态规划很有收益。如果每次展开都是不同的解&#…...

Linux中的进程间通信之共享内存

共享内存 共享内存示意图 共享内存数据结构 struct shmid_ds {struct ipc_perm shm_perm; /* operation perms */int shm_segsz; /* size of segment (bytes) */__kernel_time_t shm_atime; /* last attach time */__kernel_time_t shm_dtime; /* last detach time */__kerne…...

第18周 3-过滤器

过滤器&#xff08;Filter&#xff09;概念总结 什么是过滤器 过滤器&#xff08;Filter&#xff09;是Java Web应用中用于统一拦截和处理请求的组件&#xff0c;类似于现实生活中的空气净化器或安检。它通过对请求进行前置处理&#xff0c;确保请求符合特定要求。 过滤器的…...

Linux之进程概念

作者主页&#xff1a; 作者主页 本篇博客专栏&#xff1a;Linux专栏 创作时间 &#xff1a;2024年9月28日 基本概念&#xff1a; 进程说白了其实就是一个程序的执行实例&#xff0c;正在执行的程序。 在内核层面来说&#xff0c;就是一个担当分配资源&#xff08;CPU时间…...

小程序-使用npm包

目录 Vant Weapp 安装 Vant 组件库 使用 Vant 组件 定制全局主题样式 API Promise化 1. 基于回调函数的异步 API 的缺点 2. 什么是 API Promise 化 3. 实现 API Promise 化 4.调用 Promise 化之后的异步 API 小程序对 npm 的支持与限制 目前&#xff0c;小程序中已经…...

【springboot】整合沙箱支付

目录 1. 配置沙箱应用环境2. 配置springboot项目1. 引入依赖2. 配置文件注册下载ngrok 3. 创建支付宝支付服务类4. 支付界面模板5. 控制类实现支付6. 测试 1. 配置沙箱应用环境 使用支付宝账号登录到开放平台控制台。 使用支付宝登录后&#xff0c;看到以下页面&#xff0c;下…...

技术速递|Python in Visual Studio Code 2024年9月发布

排版&#xff1a;Alan Wang 我们很高兴地宣布将于 2024 年 9 月发布适用于 Visual Studio Code 的 Python 和 Jupyter 扩展&#xff01; 此版本包括以下公告&#xff1a; Django 单元测试支持使用 Pylance 从 inlay 提示转到定义 如果您有兴趣&#xff0c;可以在我们的 Pyth…...

数据结构-3.5.队列的顺序实现

一.队列的顺序实现&#xff0c;初始化操作以及判断队列是否为空&#xff1a; 1.图解&#xff1a; 2.代码&#xff1a; #include<stdio.h> #define MaxSize 10 //定义一个队列最多存储的元素个数 ​ typedef struct {int data[MaxSize]; //用静态数组存放队列元素int f…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

vscode(仍待补充)

写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh&#xff1f; debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...