当前位置: 首页 > 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…...

preconnect 预解析

preconnect 是一种浏览器优化技术&#xff0c;用于告诉浏览器提前与指定的域名建立连接&#xff0c;包括DNS解析、TCP握手和TLS协商&#xff08;如果适用&#xff09;。这样做可以减少客户端在请求资源时所需的往返时间&#xff08;RTT&#xff09;&#xff0c;从而提高页面加载…...

Leecode热题100-283.移动零

给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0]示例 2: 输入: nums [0] 输出: […...

如何高效使用Prompt与AI大模型对话

一、如何与人工智能对话 在人工智能的世界里&#xff0c;提示词&#xff08;Prompt&#xff09;就像是一把钥匙&#xff0c;能够解锁AI智能助手的潜力&#xff0c;帮助你更高效地获取信息、解决问题。但如何正确使用这把钥匙&#xff0c;却是一门艺术。本文将带你了解提示词的…...

Java 之深入理解 String、StringBuilder、StringBuffer

前言 由于发现 String、StringBuilder、StringBuffer 面试的时候会经常问到&#xff0c;这里就顺便总结一下&#xff1a;本文重点会以这三个字符串类的性能、线程安全、存储结构这三个方面进行分析 ✨上期回顾&#xff1a;Java 哈希表 ✨目录 前言 String 介绍 String 的不可变…...

vue3项目执行pnpm update后还原package.json文件后运行报错

项目场景&#xff1a; vue官方版本已更新到vue3.5&#xff0c;项目中还在使用vue3.4&#xff0c;因此想要更新项目vue版本。 问题描述 执行了 pnpm update 命令&#xff0c;一键更新了所有包&#xff0c;更新完成后项目不能正常运行。为了还原项目代码&#xff0c;先删除 nod…...

蓝桥杯【物联网】零基础到国奖之路:十七. 扩展模块之单路ADC和NE555

蓝桥杯【物联网】零基础到国奖之路:十七. 扩展模块之单路ADC和NE555 第一节 硬件解读第二节 CubeMx配置第三节 代码1&#xff0c;脉冲部分代码2&#xff0c;ADC部分代码![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/57531a4ee76d46daa227ae0a52993191.png) 第一节 …...

SolveigMM Video Splitter方便快捷视频分割合并软件 V3.6.1309.3-供大家学习研究参考

视频分割功能(Splitter)支持各种编码格式的AVI(DivX、DV、MJPEG、XVID、MPEG-4)、WMV、ASF(DivX、MJPEG、XVID、MPEG-4、WM Video 7/9)F、MPEG(*.mpg、*.mpeg、*.mpv、*.m2v、*.vob)文件、也支持受损的WMV、ASF格式的分割。视频合并功能(Joiner)则支持AVI、WMV/ASF、WMA、MP3、…...

Unity3D 创建一个人物,实现人物的移动

1&#xff0c;创建项目 首先打开我们的Unity Hub 在我们的编译器下面新建项目&#xff0c;选择3D模板&#xff0c;更改一下我们的项目名称&#xff0c;选择一下路径&#xff0c;然后点击创建项目 等待项目创建。。。。。。 我们在项目里先创建一个plane&#xff0c;这样有点视…...

【笔记】数据结构12

文章目录 2013年408应用题41方法一方法二 看到的社区的一个知识总结&#xff0c;这里记录一下。 知识点汇总 2013年408应用题41 解决方法&#xff1a; 方法一 &#xff08;1&#xff09;算法思想 算法的策略是从前向后扫描数组元素&#xff0c;标记出一个可能成为主元素的元…...

django的URL配置

1 django如何处理一个请求 首先Django要使用根URLconf模块&#xff0c;通过setting.py配置文件的ROOT_URLCONF来设置。 加载该模块后并查找变量 urlpatterns。这是一个Python的django.conf.urls.url()实例列表。 Django按顺序运行每个URL模式&#xff0c;并在匹配所请求的…...

蓝海国际版网站建设/西安今日头条最新新闻

1. In the afternoon, an elder practices Taijiquan in the garden of Cihui Lou.图片&#xff1a;下午&#xff0c;一位老年性痴呆患者正在“慈慧之家”的花园里练习太极拳。bbs.haibao.cn2. An old man waltzes with a nurse. Some old melodies are often played in Cihui …...

电子商务网站建设的基本要素/品牌推广公司

Ractor 一个基于 action 设计的状态管理工具 安装 npm i ractor 复制代码快速了解 Ractor Ractor 仅仅包含三个基本概念&#xff0c;您只需要熟悉这三样东西就能彻底掌握 Ractor 的用法。 System 和其他库有所区别&#xff0c; Ractor 的事件系统和逻辑系统是分开的&#xff0c…...

做外贸是什么网站/百度账号注册平台

雷帝网 乐天 9月18日报道9月18日&#xff0c;阿里巴巴2018全球投资者大会进入第二天。这是阿里巴巴宣布“传承计划”之后的第一次投资者大会。阿里巴巴执行副主席蔡崇信向与会投资人阐释了阿里巴巴合伙人制度的三大特征&#xff1a;树立道德标准&#xff0c;解决接班人问题&…...

静态网站有哪些优点/企业网站源码

rui888 推荐的&#xff0c;发现挺不错&#xff0c;留着以后可以用 原文地址&#xff1a;http://www.cnblogs.com/dycg/archive/2010/04/21/1717502.html 该组件对中文支持良好&#xff0c;支持单文件与多文件的同时上传 主要类介绍 DiskFIleItemFactory类 &#xff0c;该类创建…...

海外seo网站建设/sem是什么

我正在用JPA项目做一些EJB,它将一些实体映射/持久化到mysql数据库.我在persistence.xml中定义了持久性单元,如下所示&#xff1a;org.apache.openjpa.persistence.PersistenceProviderImplMyAppDS然后,在tomee / conf / tomee.xml文件中,我已经定义了这样的数据源&#xff1a;J…...

网站维护内容和方法/电商网站建设公司哪家好

在学习Servlet过程中&#xff0c;针对图片上传做了一个Demo&#xff0c;实现的功能是&#xff1a;在a页面上传图片&#xff0c;点击提交后&#xff0c;将图片保存到服务器指定路径(D:/image)&#xff1b;跳转到b页面&#xff0c;b页面读取展示绝对路径(D:/image)的图片。主要步…...