字节Coze实现多Agent模式,文内在线体验,实时给产品经理提需求_coze 多agent-CSDN博客

mikel阅读(778)

来源: 字节Coze实现多Agent模式,文内在线体验,实时给产品经理提需求_coze 多agent-CSDN博客

摘要:
多Agent模式是一种分布式计算范式,它通过将复杂任务分解为多个子任务,并由独立的智能体(Agents)并行处理,从而提高系统的处理能力和效率。这种模式在自然语言处理、机器学习和其他数据密集型应用中尤为有效。然而,多Agent系统的实施和运行需要显著的算力支持,尤其是在处理大型数据集和复杂模型时。为了满足这些需求,算力提供方正在探索新的供给模式,软硬件优化,存内计算等模式。

AI Agent 设计模式:
吴恩达教授在美国红杉 AI 活动上关于 Agent 的最新趋势与洞察,提出了目前有 4 种主要的 Agent 设计模式,分别是:

Reflection:让 Agent 审视和修正自己生成的输出;

Tool Use:LLM 生成代码、调用 API 等进行实际操作;

Planning:让 Agent 分解复杂任务并按计划执行;

Multiagent Collaboration:多个 Agent 扮演不同角色合作完成任务;

🤩 在 扣子(coze.cn)/Coze (coze.com)上,可以将上述四种模式快捷落地,本文重点介绍Reflection模式和多Agent模式。

1 Reflection模式
含义:让 Agent 审视和修正自己生成的输出。

背景:大模型的生成有时候会犯懒,可能只会部分执行Prompt导致效果有限。Reflection模式适用于让LLM自行审视和修正自己生成的输出,对生成内容进行多次自我调优,进而生成更加优质的内容。

场景:让 AI 或 LLM 说,写一个行业短评。开始写第一稿,自己阅读生成第一稿,思考哪些部分需要修改,然后,LLM进一步优化生成,可以一遍又一遍地进行。因此,这个工作流程是可迭代的,你可能让模型进行一些思考,然后修改文章,再思考,并通过多次迭代来完成这个过程。

流程图:

 

🔥 Workflow实现简单“行业短评”效果
👉 工作流拆解:

第一步:Start节点,用于接收用户的输入。

第二步: 大模型节点,行业短评Prompt:使用真实数据、案例、SWOT模型,并言简意赅表达。

第三步:基于其生成结果第二步中的大模型的生成结果,复制其Prompt,并进一步提示生成短评,达到审视和修正自己生成输出的效果,提高短评生成质量。

第四步: 输出结果。

👉 示意图参考:

 

👀 效果:汽车行业调研短评

 

第一次:大模型对于提示,仅生成比较概括性的短评,使用了真实数据。

 

第二次:大模型对生成结果进行迭代,生成了详细短评,不仅优化了表达内容,还增加了SWOT分析和案例分析。

 

2 Multiagent Collaboration 模式
🔥 使用coze的Multi-agent功能实现高质量旅行规划
第一步:定义3个用于旅行规划场景的专家Agents

目的地推荐专家: 调用搜索等能力,基于用户的需求推荐目的地。

机票酒店专家: 调用机票、酒店的查询工具,根据用户的背景信息和诉求,推荐合适的机票酒店。

行程规划专家: 根据用户的信息和其他专家产出的结果,帮助用户制定完整的行程规划,并将内容输出到PDF中。

第二步:将3个专家Agents排列到画布中,并为他们设置任务交接的条件。

第三步:开始对话

 

开放体验来袭:
直接登入Coze,创建属于你的Agent, 产品经理在线等你提需求,点击下方链接,开启测评:
https://bbs.csdn.net/topics/618354373
https://bbs.csdn.net/topics/618354373

 

效果演示图

多Agent模式的算力需求
多Agent模式在构建AI系统时,需要处理来自不同代理的任务和数据,这通常会导致算力需求的增加。在多Agent模式下,每个代理可能负责特定的任务或工作流程的一部分,它们需要进行通信和协作以完成更复杂的任务。这种模式的算力需求取决于多个因素,包括代理的数量、每个代理处理的任务复杂度、代理间的交互频率以及整体系统的优化程度。

软硬件层面的优化。
1. 硬件层面:
使用高性能的计算硬件,如多核CPU、GPU或TPU,以支持并行处理和快速的数据处理。
考虑使用高速网络接口和交换机,以减少Agent间通信的延迟。
采用分布式存储系统,如分布式数据库或对象存储,以高效管理大量数据。
2. 软件架构:
采用微服务架构,每个Agent可以作为一个独立的微服务运行,这样可以提高系统的可伸缩性和容错性。
实现一个高效的任务调度器,如FIFO或轮询(Round-Robin)调度算法,以合理分配任务和资源。
引入上下文管理器,以支持LLM的上下文快照和恢复,管理LLM的上下文窗口,确保Agent间的有效协作。
使用内存管理器来为每个Agent的交互日志提供短期内存,确保数据在Agent活跃时存储和可访问。
集成工具管理器,管理一系列API工具,增强LLM的功能,如网络搜索、科学计算等。
3. 操作系统层面:
考虑开发或使用专为多Agent系统设计的操作系统,如AIOS,它将LLM嵌入到操作系统中,优化Agent请求的调度,支持上下文切换,实现并发执行,并提供工具服务和访问控制。
4. 开发工具和框架:
利用多Agent框架,如AutoGen,它允许用户创建和管理多个智能体,以协同完成复杂的任务。
使用提供多Agent支持的开发工具包(SDK),简化Agent开发者的开发过程,专注于Agent的核心逻辑和功能。
存内计算架构火力支持
在多Agent模式中,如果每个代理都使用类似的大型模型,那么总体算力需求将是巨大的。此外,多模态大模型对算力的消耗呈指数级增长趋势,因为它们需要处理和整合来自不同模态(如文本、图像、音频等)的数据。这种类型的模型在多Agent模式下的应用将进一步推动对高性能计算资源的需求。

值得一提的是,在新一轮算力攻坚赛中,突破传统冯·诺依曼架构的范式探索成为主要方向之一。而“存算一体”架构打破了存算分离的壁垒,减少了数据的搬运,它就如同“在家办公”的新型工作模式,消除了数据“往返通勤“的能量消耗、时间延迟,并且节约了“办公场所”的运营成本,因而具备高能效比。加上“存算一体”架构对于工艺制程的“弱依赖”性(14nm展现4nm数字电路表现性能),使其成为了AI算力的重要发展方向。

Posit数字计算内存(CIM)模型(简称PD-CIM)的整体架构

上图为最新融入Posit系统的存内计算架构,在加载数据到PD-CIM时,FP-to-POSIT编码器将浮点数据转换为Posit格式以便于存储。CPCS会检测权重尾数的位宽,并预存逻辑值到备用的CIM单元中。在计算过程中,BRPU执行基于位移位或的区间处理。CIM核心结合CPCS有三种工作模式,包括3位模式、2-4位集合关联模式和正常模式。前两种模式使用预存的逻辑值来实现CIM内部的双比特乘累加(MAC)操作。CASU通过将加法操作替换为位级或操作来节省加法树的能量,并改变CPCS的计算顺序以增加无重叠尾数的数量。

整体而言,PD-CIM架构的设计旨在通过在存储器内部直接进行计算来提高能效和计算性能,同时支持多种计算精度和工作模式,以适应不同的计算需求。这种架构特别适合于需要处理大量数据和复杂计算任务的应用,如人工智能和大数据分析。

总结:
多Agent模式背后的算力需求通常较高,因为需要同时处理来自多个智能体的任务和数据。存内计算(Computing in memory,CIM)作为一种新兴的计算架构,能够在存储器阵列内完成逻辑运算,避免存储器和处理器之间频繁的数据搬移操作,从而提升算力,降低功耗。
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/m0_58966968/article/details/137691787

我用OpenGlass做了一个AI眼镜【更新中】 | Rocket Lu

mikel阅读(456)

来源: 我用OpenGlass做了一个AI眼镜【更新中】 | Rocket Lu

最近看见了一个开源眼镜的代码,而且需要的硬件只要100块左右,于是准备复刻一个。
notion image

准备工作

需要的硬件是一个芯片、一个电池、一个3D打印的外壳。
硬件拿到手后,可以开始软件工作了。
01 下载Arduino IDE
我在整合包里准备好了,大家可以直接回复“Glass”下载
02 下载Arduino IDE
打开 firmware folder 文件夹并在 Arduino IDE 中打开 .ino 文件。
按照软件准备步骤为 XIAO ESP32S3 板设置 Arduino IDE:
notion image
    • 将 ESP32 板 添加到您的 Arduino IDE:
      • Navigate to File > Preferences, and fill “Additional Boards Manager URLs” with the URL: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
导航到“文件”>“首选项”,然后使用以下 URL 填写“其他 Boards Manager URL”: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
      • Navigate to Tools > Board > Boards Manager…, type the keyword esp32 in the search box, select the latest version of esp32, and install it.
导航到“工具”>“开发板”>“开发板管理器…”,在搜索框中输入关键字 esp32 ,选择最新版本的 esp32 并安装它。
    • Select your board and port:
选择您的主板和端口:
      • On top of the Arduino IDE, select the port (likely to be COM3 or higher).
在 Arduino IDE 顶部,选择端口(可能是 COM3 或更高端口)。
      • Search for xiao in the development board on the left and select XIAO_ESP32S3.
在左侧开发板中搜索 xiao 并选择 XIAO_ESP32S3 。

开启智能眼镜新时代:OpenGlass-CSDN博客

mikel阅读(518)

来源: 开启智能眼镜新时代:OpenGlass-CSDN博客

开启智能眼镜新时代:OpenGlass
项目地址:https://gitcode.com/BasedHardware/OpenGlass

项目简介
OpenGlass是一个创新的开源项目,它能将普通眼镜转变为功能强大的智能眼镜,成本只需25美元以下。利用现成的组件,你可以记录生活、记住见过的人、识别物体、翻译文本等,让你的视野从此智能起来。

 

通过视频演示,进一步了解OpenGlass的神奇之处。

技术分析
OpenGlass的核心是基于Seeed Studio XIAO ESP32 S3 Sense微控制器,这是一款集成了Wi-Fi和蓝牙功能的高性能芯片。结合EEMB LP502030电池和3D打印的眼镜架,构建出轻便且持久使用的智能眼镜框架。软件部分则采用了Node.js和Expo开发的移动端应用,实现了与硬件的无缝交互,为用户提供直观易用的界面。

关键功能实现依赖于各种API服务,如Groq和OpenAI用于数据处理和智能识别,而Ollama的REST API用于语音转文本功能。为了确保隐私,自托管选项也得到了支持。

应用场景
OpenGlass的应用广泛:

生活记录者: 随时随地记录你的日常生活,重要时刻不再错过。
社交达人: 记住新朋友的名字,减少尴尬的忘记瞬间。
户外探险: 辨识野生动植物,增加旅行的乐趣。
语言翻译: 实时翻译文字,跨国交流更无障碍。
视觉辅助: 对视力受限的人来说,它可以读取菜单或标签上的文字。
项目特点
低成本: 无需高昂费用,就能拥有智能眼镜体验。
可扩展性: 开源设计允许开发者和爱好者添加更多功能。
便捷安装: 易于组装的硬件和清晰的软件设置指南,任何人都可以尝试。
社区支持: 加入Based Hardware Discord社区,获取帮助、分享经验,共同进步。
隐私优先: 自主选择是否自托管API服务,尊重并保护个人数据。
如果你对预装版感兴趣,可以填写兴趣表单获得通知。现在就动手,开启属于你的智能眼镜时代吧!

注:文章中的链接可能无法直接点击,请复制到浏览器中打开。
1
项目地址:https://gitcode.com/BasedHardware/OpenGlass
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/gitblog_00012/article/details/139019634

uniapp table 组件-CSDN博客

mikel阅读(260)

来源: uniapp table 组件-CSDN博客

uniapp table 组件

滑动验证页面

 

  • uniapp是2019年非常的火爆的一个Dcloud开发跨平台前端工具,支持ios Android wap,小程序,除了Android有点卡外,其他暂时没有发现什么瑕疵。
  • 最近在捣鼓uniapp项目,恰好用到table组件,之前写了个demo,后面一直都想写一个像样的,可以分享给别人用的组件。
  • 自己的水平一般,开发出来的组件可能代码不咋地,还望路过大神斧正。
  • 下面是我写的uniapptable的说明,希望能够帮到别人

这里默认大家都非常熟悉vue和uniapp的包引入,不再阐述uniapp组件引入方法以及uniapp组件和vue的区别

插件市场更新会比github更新延后,查看github项目:

  • uniapp是2019年非常的火爆的一个Dcloud开发跨平台前端工具,支持ios Android wap,小程序,除了android有点卡外,其他暂时没有发现什么瑕疵。
  • 最近在捣鼓uniapp项目,恰好用到table组件,之前写了个demo,后面一直都想写一个像样的,可以分享给别人用的组件。
  • 自己的水平一般,开发出来的组件可能代码不咋地,还望路过大神斧正。
  • 下面是我写的uniapptable的说明,希望能够帮到别人

这里默认大家都非常熟悉vue和uniapp的包引入,不再阐述uniapp组件引入方法以及uniapp组件和vue的区别

插件市场更新会比github更新延后,查看github项目 GitHub – MTTTM/uniapp-elemnt-table: 模仿element-ui的表格组件

插件市场更新会比github更新延后,查看demo源码

插件市场更新会比github更新延后,查看demo使用引入方式

支持功能大概如下

点击查看demo,PC浏览器模拟显示和真实移动设备访问有差异

行合并

列合并

固定table高度

数据加载

数据为空

自定义行样式

自定义某行样式

自定义某行某列某个单元格样式

使用插槽自定义单元格内容

数据多选操作

数据单选操作

数据删除&&数据编辑或者更多操作方式

固定右边一列

固定左边一列

固定高度不支持同时固定某一列

已测试平台 ->ios,android,wap,微信小程序

部分效果预览

图片描述
图片描述
图片描述
图片描述
图片描述
图片描述

基本使用示例源码

html

<v-table :columns="columns" :list="data"></v-table>

JavaScript (data数组必须提供id)

  1. import vTable from “@/components/table.vue”
  2. export default{
  3. components: {
  4. vTable
  5. },
  6. data(){
  7. return {
  8. data: [{
  9. name: ‘John Brown’,
  10. age: 18,
  11. address: ‘New York No. 1 Lake Park’,
  12. id: “1”,
  13. }
  14. ],
  15. columns: [{
  16. title: “ID”,
  17. key: “id”
  18. },
  19. {
  20. title: ‘Name’,
  21. key: ‘name’
  22. },
  23. {
  24. title: ‘Age’,
  25. key: ‘age’
  26. },
  27. {
  28. title: ‘Address’,
  29. key: ‘address’
  30. }
  31. ],
  32. }
  33. }
  34. }

基本属性

html标签属性

属性 说明 类型 默认 必填
columns 列数据 Array 必填
dataColSpan 行数据 Array 必填
row-class-name 行样式 String或Function
height 表格高度(可用固定表头) Number
td-height 单元格高 Number 110
td-width 单元格宽 Number 30
td-padding 单元格间距 Number 10
border-color 表格边框颜色 String #666
th-td-height 表头单元格高 Number 30
loading 加载状态 Boolean false
selection 可选mulit和single String
span-method 行列合并 Function
slot-cols 插槽自定义列元素,对应columns的key Array
emptyText 插没数据提示文字 String
emptyClickFn 没数据点击响应函数 Function

事件

属性 说明 参数
@on-selection-change 单选 多选变更 {old:””,new:””}
@delete 自定义事件(它可以不叫delete,可以是任意事件,只要你在list里面定义了) {row:{},index:Number}

list 参数

属性 说明 类型
cellClassName 设置行内某一列的样式类名 Object
operate 数据操作的列 Object

columns 参数

属性 说明 类型
$width 设置列宽度 例如 “120px”,请务必使用px单位 String
$fixed 固定某一列,可选left和right String
$operateList 数据操作选项和list的operate对应 Array

插件市场更新会比github更新延后,查看demo源码

插件市场更新会比github更新延后,查看demo使用引入方式

支持功能大概如下

点击查看demo,PC浏览器模拟显示和真实移动设备访问有差异

行合并

列合并

固定table高度

数据加载

数据为空

自定义行样式

自定义某行样式

自定义某行某列某个单元格样式

使用插槽自定义单元格内容

数据多选操作

数据单选操作

数据删除&&数据编辑或者更多操作方式

固定右边一列

固定左边一列

固定高度不支持同时固定某一列

已测试平台 ->ios,android,wap,微信小程序

部分效果预览

图片描述
图片描述
图片描述
图片描述
图片描述
图片描述

基本使用示例源码

html

<v-table :columns="columns" :list="data"></v-table>

JavaScript (data数组必须提供id)

  1. import vTable from “@/components/table.vue”
  2. export default{
  3. components: {
  4. vTable
  5. },
  6. data(){
  7. return {
  8. data: [{
  9. name: ‘John Brown’,
  10. age: 18,
  11. address: ‘New York No. 1 Lake Park’,
  12. id: “1”,
  13. }
  14. ],
  15. columns: [{
  16. title: “ID”,
  17. key: “id”
  18. },
  19. {
  20. title: ‘Name’,
  21. key: ‘name’
  22. },
  23. {
  24. title: ‘Age’,
  25. key: ‘age’
  26. },
  27. {
  28. title: ‘Address’,
  29. key: ‘address’
  30. }
  31. ],
  32. }
  33. }
  34. }

基本属性

html标签属性

属性 说明 类型 默认 必填
columns 列数据 Array 必填
dataColSpan 行数据 Array 必填
row-class-name 行样式 String或Function
height 表格高度(可用固定表头) Number
td-height 单元格高 Number 110
td-width 单元格宽 Number 30
td-padding 单元格间距 Number 10
border-color 表格边框颜色 String #666
th-td-height 表头单元格高 Number 30
loading 加载状态 Boolean false
selection 可选mulit和single String
span-method 行列合并 Function
slot-cols 插槽自定义列元素,对应columns的key Array
emptyText 插没数据提示文字 String
emptyClickFn 没数据点击响应函数 Function

事件

属性 说明 参数
@on-selection-change 单选 多选变更 {old:””,new:””}
@delete 自定义事件(它可以不叫delete,可以是任意事件,只要你在list里面定义了) {row:{},index:Number}

list 参数

属性 说明 类型
cellClassName 设置行内某一列的样式类名 Object
operate 数据操作的列 Object

columns 参数

属性 说明 类型
$width 设置列宽度 例如 “120px”,请务必使用px单位 String
$fixed 固定某一列,可选left和right String
$operateList 数据操作选项和list的operate对应 Array
文章知识点与官方知识档案匹配,可进一步学习相关知识

移动端 触摸事件和mousedown、mouseup、click事件之间的关系_移动端 touch、click出发顺序-CSDN博客

mikel阅读(282)

来源: 移动端 触摸事件和mousedown、mouseup、click事件之间的关系_移动端 touch、click出发顺序-CSDN博客

一、移动端 触摸事件

ontouchstart、ontouchmove、ontouchend、ontouchcancel

1、Touch事件简介

pc上的web页面鼠 标会产生onmousedown、onmouseup、onmouSEOut、onmouSEOver、onmousemove的事件,但是在移动终端如iphone、ipod Touch、ipad上的web页面触屏时会产生ontouchstart、ontouchmove、ontouchend、ontouchcancel事件,分别对应了触屏开始、拖拽及完成触屏事件和取消。
当按下手指时,触发ontouchstart;
当移动手指时,触发ontouchmove;
当移走手指时,触发ontouchend。
当一些更高级别的事件发生的时候(如电话接入或者弹出信息)会取消当前的touch操作,即触发ontouchcancel。一般会在ontouchcancel时暂停游戏、存档等操作。

2、Touch事件与Mouse事件的出发关系

在触屏操作后,手指提起的一刹那(即发生ontouchend后),系统会判断接收到事件的element的内容是否被改变,如果内容被改变,接下来的事 件都不会触发,如果没有改变,会按照mousedown,mouseup,click的顺序触发事件。特别需要提到的是,只有再触发一个触屏事件时,才会 触发上一个事件的mouSEOut事件。

二、mousedown、mouseup、click事件之间的关系

点击select标签元素的时候,会弹出下拉。然而当option中没有元素时,就不希望弹出下拉(比如在某些浏览器中,点击select会默认出一个罩层效果,而此时没有数据选择的话,弹出比较不友好)。

首先想到是利用click事件控制,发现仍然会有下拉出现…实际这个是mousedown事件控制的。

这里就说明下click和mousedown、mouseup。规范要求,只有在同一个元素上相继触发 mousedown 和 mouseup 事件,才会触发 click 事件;如果 mousedown 或 mouseup 中的一个被取消,就不会触发 click 事件。

这句话也很好理解,有时候我们在浏览网页时,鼠标在一个按钮或者链接上按下了,但是突然却又改了主意,此时我们一般会移开鼠标,在另一个空白处松开鼠标哈哈~相信这个大家经常上网都有经验。实际这个就利用了click事件要求在同一个元素相继触发mousedown 和 mouseup 事件。

<script type=”text/JavaScript”>
var len = 0;
$(‘#sel’).mousedown(function(){
if(len == 0){// 模拟一下select标签没数据的标志
console.log(‘mousedown’);
return false;
}
}).mouseup(function(){
console.log(‘mouseup’);
}).click(function(){
console.log(‘click’);
});
</script>
点击后发现,log的顺序是:mousedown–>mouseup–>click

当在mousedown中return false后,就不会弹出下拉或者罩层了…

 

这里再介绍下鼠标的各个事件:

DOM3 级事件中定义了9个鼠标事件,简介如下。
click:在用户单击主鼠标按钮(一般是左边的按钮)或者按下回车键时触发。这一点对确保易访问性很重要,意味着onclick事件处理程序既可以通过键盘也可以通过鼠标执行。

dblclick:在用户双击主鼠标按钮(一般是左边的按钮)时触发。从技术上说,这个事件并不是DOM2级事件规范中规定的,但鉴于它得到了广泛支持,所以DOM3 级事件将其纳入了标准。

mousedown:在用户按下了任意鼠标按钮时触发。不能通过键盘触发这个事件。

mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。DOM2级事件并没有定义这个事件,但 DOM3级事件将它纳入了规范。IE、Firefox 9+和 Opera支持这个事件。

mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。DOM2级事件并没有定义这个事件,但 DOM3级事件将它 纳入了规范。IE、Firefox 9+和 Opera支持这个事件。

mousemove:当鼠标指针在元素内部移动时重复地触发。不能通过键盘触发这个事件。

mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入的另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。不能通过键盘触发这个事件。

mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触 发。不能通过键盘触发这个事件。

mouseup:在用户释放鼠标按钮时触发。不能通过键盘触发这个事件。 页面上的所有元素都支持鼠标事件。除了 mouseenter 和 mouseleave,所有鼠标事件都会冒泡, 也可以被取消,而取消鼠标事件将会影响浏览器的默认行为。取消鼠标事件的默认行为还会影响其他事 件,因为鼠标事件与其他事件是密不可分的关系。
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/muzidigbig/article/details/83276851

html页面实现自动适应手机浏览器(一行代码搞定)_layui html兼容手机-CSDN博客

mikel阅读(286)

来源: html页面实现自动适应手机浏览器(一行代码搞定)_layui html兼容手机-CSDN博客

    在互联网技术飞速发展的今天,用户通过各种设备上网,从桌面计算机、笔记本电脑到智能手机和平板电脑,每个设备的屏幕尺寸都不同。这就要求网站设计师和开发人员采用响应式设计,以确保用户在任何设备上都能获得最佳的网站访问体验。要实现这一点,<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>这一简单但至关重要的元标签起到了关键作用。
   直接在<head>头标签中,添加    <meta name=”viewport” content=”width=device-width, initial-scale=1.0″>这行代码就可
示例:
<head>
    <meta charset=”UTF-8″>
<!– 适应手机浏览器屏幕 –>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
    <title>测试</title>
    <link rel=”stylesheet” href=”style.css”>
   <link rel=”stylesheet” href=”../../layui/css/layui.css” media=”all”>
</head>
一、什么是Viewport?
Viewport是用户在网页上可见区域的部分。在移动设备上浏览时,viewport的宽度通常小于网页的宽度,因此浏览器会缩放页面以适应屏幕。这就导致了用户需要放大、缩小和滚动才能浏览网站的情况,这显然不是一个理想的浏览体验。
二、Viewport 元标签的作用
Viewport 元标签允许网页开发者控制viewport的大小和比例。通过使用这个标签,网站可以指示浏览器如何响应不同尺寸的屏幕。这个元标签应该被添加到HTML文档的<head>部分中。
举例来说,添加了<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>的网页会告诉浏览器:
width=device-width:将viewport的宽度设置为跟随设备屏幕的宽度。这意味着页面的宽度将自动调整以匹配设备屏幕的宽度,无论设备的宽度有多大或多小。
initial-scale=1.0:设置初始缩放比例为1.0,这表示页面初始加载时将按照实际大小显示,不会进行任何缩放。
三、响应式设计的好处
采用响应式设计,尤其是合理使用viewport元标签,可以带来许多好处:
用户体验:确保所有用户都能无论使用哪种设备都能获得良好的浏览体验。
维护成本:不需要创建多个版本的网站以适应不同设备,一个网站适用所有屏幕尺寸。
SEO优势:搜索引擎优先考虑适用于移动设备的网站,在搜索引擎结果页(SERPs)上有更好的排名。
四、实施响应式设计
除了在HTML中实现viewport元标签,响应式设计还包括其他技术和方法,例如:
使用百分比而不是固定的像素宽度来布局页面。
媒体查询(Media Queries),允许CSS根据设备的特定特征来应用不同的样式。
灵活图片和网格布局,图片和其他元素需要在不同屏幕尺寸上适当地缩放。
   总之,viewport元标签是实现响应式设计中不可或缺的一个环节,配合其他的前端技术手段,能提供跨设备一致性的访问体验。随着移动设备使用的日益普及,掌握并实施这些技术策略,对于每一个希望在数字领域保持竞争力的企业来说都是致关重要的。
————————————————
                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_53607413/article/details/135289002

华为S2300系列交换机配置web登录界面

mikel阅读(375)

来源: 华为S2300系列交换机配置web登录界面

最近感觉自己太菜了 搞了几台设备组网玩 过几天AR1200到手 到时候折腾路由xD

整天连着根console有点傻,想搞个web界面来管理,于是就有了这篇水文~

设备型号:Quidway  S2326TP-EI (线下淘来的 还蛮新)

Desc: Quidway S2326TP-EI-CX4Z126AM-S2326TP-EI Mainframe(24 10/100 BASE-T ports and 2 Combo GE(10/100/1000 BASE-T+100/1000 Base-X) ports and AC 110/220V)

首先既然是web console,我们需要一个IP地址,可以利用vlanif功能来实现给交换机配置一个IP地址。

首先 初始配置的情况下需要通过交换机的console口登录到设备上进行配置 。

首先在用户模式下

<Quidway> system-view //进入system-view
[Quidway] vlan 1
[Quidway] interface vlanif 1
[Quidway-Vlanif1] ip address 172.28.96.40 27 //IP和掩码可以自己定义,和PC同网段即可。
[Quidway-Vlanif1] quit

之后要把web控制台相关的文件通过FTP上传到交换机里,因为只有16M的FLASH,默认没有带web控制台的文件xD

<Quidway> system-view
[Quidway]ftp server enable //启动FTP服务
[Quidway]aaa //配置AAA认证
[Quidway-aaa] local-user admin password cipher 123456 //配置登录用户名、鉴权方式、密码加密方式、密码
[Quidway-aaa] local-user admin service-type ftp //配置用户admin的登录类型
[Quidway-aaa] local-user admin privilege level 15 //配置用户admin的登录权限。注意默认登录权限是3,很可能造成登录失败,建议设置为15
[Quidway-aaa] local-user admin privilege level 15 //配置用户admin的默认FTP目录,此处设置为闪存根目录
[Quidway-aaa] return
[Quidway]user-interface vty 0 4 //进入认证配置模式
[Quidway-ui-vty0-4]authentication-mode aaa //设置AAA认证

之后用电脑登录交换机的FTP服务器上传web控制台文件即可。(二者须同网段,电脑不一定要插在交换机上)

C:\Users\IPP>ftp 172.28.96.40
连接到 172.28.96.40。
220 FTP service ready.
530 Please login with USER and PASS.
用户(172.28.96.40:(none)): ipp
331 Password required for ipp.
密码:
230 User logged in.
ftp> put D:\web.zip
200 Port command okay.
150 Opening ASCII mode data connection for web.zip.
226 Transfer complete.
ftp: 发送 132897 字节,用时 4.53秒 29.34千字节/秒。
ftp> bye
221 Server closing.

此时文件已经成功上传到设备闪存,退出FTP。

在交换机用户模式下通过命令dir确认是否存在web.zip文件,存在则表示文件正确传输到设备上。

之后继续在console操作。准备载入控制台文件,启动HTTP服务

<Quidway> system-view
[Quidway]http server load web.zip //使http服务器加载zip包
[Quidway]http server enable //启动http服务器
[Quidway]Undo ftp server //关闭FTP服务器
[Quidway]quit
reboot
Info: The system is now comparing the configuration, please wait. Info: Save current configuration?[Y/N]:
//连续两个Y保存配置并重启交换机

重启后检查http服务是否已经启动,之后即可用IP登录web控制台,S2300系列没有HTTPS~

C#.Net筑基-模式匹配汇总 - 安木夕 - 博客园

mikel阅读(253)

来源: C#.Net筑基-模式匹配汇总 – 安木夕 – 博客园

image.png

01、模式匹配概述

C#7开始支持的 模式匹配 语法(糖,挺甜),可非常灵活的对数据进行条件匹配和提取,经过多个版本的完善,已经非常强大了。

C# 支持多种模式,包括声明、类型、常量、关系、属性、列表、var 和弃元等,在isswitch语句、switch表达式中使用,还可以使用布尔逻辑关键字 andor 和 not 组合多个模式,极大的简化了代码编写,可读性也还不错。

标题 说明 示例/备注
类型和声明模式 如果类型兼容,则申明并赋值变量 if (age is int i)
常量模式 检查表达式值是否等于、不等于(not)常量值 if(age is null &#124;&#124; age is 0)
关系模式>< 使用关系运算符<><= 或 >=匹配 case 0 or <=6
逻辑模式 not>and >or连接多个模式表达式 case < 12 and ( >6 or 6)
属性模式{:} 对实例的属性、字段进行模式匹配:{属性/字段:匹配模式} if (b is { Year: < 2000, Month: 1 or 11 })
位置模式(解构) 基于解构赋值进行模式匹配:(解构参数) if(point is (_,>0,>0))
var 模式 var申明(捕获)任意局部变量 if(point is var p && p.X>0)
弃元模式_ 弃元模式 来匹配任何(其他)表达式 表示不要的
列表模式[] 对数组(列表)进行匹配,在中括号[]中匹配列表中的项 if(numbers is [_, 2, 3, ..])

📢 模式匹配基本都是语法糖,味道都不错!C#在编译时会输出原本的基础代码,可通过 https://sharplab.io/ 在线查看编译后的代码。


02、模式匹配

2.1、类型和声明模式

检查类型是否匹配,同时申明变量,如果类型兼容则申明并赋值变量,该变量在后面代码作用域中有效。

object age = “123”;
if (age is int i) //类型匹配+申明变量i
{
Console.WriteLine($”age1 = {i});
}
switch (age)
{
case string: //类型匹配
Console.WriteLine($”type is string”);
break;
case int iage: //类型匹配+申明变量iage
Console.WriteLine($”age2 = {iage});
break;
}
//上面is语句编译后的代码效果:
if (obj is int)
{
int value = (int)obj;
}

2.2、常量模式

检查表达式值是否等于、不等于(not)常量值,常量值包括字面量常量,也包括const常量值。传统的Switch语句就是常量模式匹配。

object age = null;
if (age is not null && age is 100) //age is 100 等同于 age is int && (int)age==100
{
Console.WriteLine($”age1 = {age});
}
var type = age switch{
1 or 2 or 3=>“婴儿”,
4 => “幼儿”,
null or not 5 => “unknow”,
_=>“”,
};

2.3、关系模式><

用关系运算符来匹配表达式,就是对常量数值进行大小比较运算,使用关系运算符<><= 或 >=,多个表达式可用andor连接,当然也支持括号。

object age = 6;
if (age is int n and >= 6)
{
Console.WriteLine(“666”);
}
switch (age)
{
case 0 or <=6:
Console.WriteLine(“幼儿”);
break;
case < 12 and ( >6 or 6):
Console.WriteLine(“青少年”);
break;
}

2.4、逻辑模式not/and/or

用 notand 和 or 模式连结符来创建逻辑模式,连接多个模式表达式。

  • 优先级顺序:not>and >or
  • 推荐使用(括号)显示控制优先顺序,可读性更好。
object age = 6;
if (age is int n and (not 6 or >5) )
{
Console.WriteLine(“666”);
}

2.5、属性模式{:}

对实例的属性、字段进行模式匹配,可以嵌套其他模式匹配,非常的强大,属性匹配用大括号来包装{属性/字段:匹配模式}

  • 多个属性/字段都匹配为true时,最终才会匹配成功。
  • 可以结合类型申明模式使用。
  • 可嵌套使用,会递归匹配。
DateTime birthday = new DateTime(1999, 11, 12);
if (birthday is { Year: < 2000, Month: 1 or 11 })
{
Console.WriteLine(“年龄、星座不合适”);
}
//嵌套使用
public record Point(int X, int Y);
public record Segment(Point Start, Point End);
static bool IsAnyEndOnXAxis(Segment segment) =>
segment is { Start: { Y: 0 } } or { End: { Y: 0 } };
static bool IsAnyEndOnXAxis(Segment segment) =>
segment is { Start.Y: 0 } or { End.Y: 0 };

2.6、位置模式(解构)

基于解构赋值进行模式匹配:

  • Tuplerecord 和 DictionaryEntry是内置支持解构的,关于解构赋值可参考相关内容。
  • 用括号()报装,这也是 解构(Deconstruct)的语法形式。
  • 可以嵌套其他模式匹配,如常量、关系、逻辑、属性模式等。
void Main()
{
Point point = new Point(“sam”, 12, 13);
var len = point switch
{
//类型匹配、属性模式、位置模式:Name属性必须为string,且长度为0,X、Y值为0
(string { Length: <= 0 }, 0, 0) => 0,
(_, > 0, 0) => point.X, //X值大于0,Y值为0
(_, 0, > 0) => point.Y, //Y值大于0,X值为0
(_, 10 or > 10, 10 or > 10) p => p.X * p.Y,
_ => 0,
};
}
public record Point(string Name, int X, int Y);

2.7、var 模式

var申明(捕获)任意局部变量,把表达式的结果分配给var临时变量。算是类型模式的变种,将类型名替换成了var

void Main()
{
Point point = new Point(“sam”, 12, 13);
if(point is var p && p.X>0 && p.Y>0){ //is var
Console.WriteLine(“OK”);
}
var len = point switch
{
var (_,x,y) when x>0 && y>0 => true,// var
};
}
public record Point(string Name, int X, int Y);

2.8、弃元模式_

弃元模式(Discard Pattern),字面理解就是被遗弃、没人要的。可以将弃元模式看做是一个占位符,表示一个没人用的变量,可匹配任意类型,用来简化代码。语法是用下划线“_”表示。

常用场景

  • 1、解构时的占位符。
  • 2、在Switch中匹配任意其他模式,类似default的作用。
  • 3、在out参数中占位,表示一个没人用的out参数。
  • 4、独立弃元,接收无用的表达式输出。
var tuple = new Tuple<int, int>(3, 4);
var (x, _) = tuple; //1、只需要第一个参数,其他就用“_”来占位
Console.WriteLine(x); //3
_= x switch
{
2 or <2 => “small”,
int and <18=>“young”,
_=>“other”, //2、匹配其他模式,效果同default
};
int.TryParse(“”, out _); //3、不用的out变量,实际上是申明了变量的
async void Print(object arg)
{
_ = arg ?? throw new ArgumentException(); //4、接收无用的返回,效果同下
if (arg == null) throw new ArgumentException();
_ = Task.Run(()=>Console.WriteLine(“task run”)); //接收一个不用的返回
}

弃元模式_是一个提供给编译器用的符号,告诉编译这个变量不用了,编译器会根据情况进行优化处理。在对out参数使用时,编译器会自动创建变量,如下代码:

int.TryParse(“”,out _);
//实际编译后的代码如下
int result;
int.TryParse(“”, out result);

image.png

📢需要注意的是 下划线_是并不是一个关键字,也能当做参数名来使用,不要混用。

2.9、列表模式[]

C#11支持的,对数组(列表)进行匹配,在中括号[]中匹配列表中的项。

  • 跳过的项可以用弃元模式_
  • 可以用数组的切片模式匹配开头、结尾的元素。
void Main()
{
int[] numbers = { 1, 2, 3, 4 };
Console.WriteLine(numbers is [_, 2, 3, ..]); // True
Console.WriteLine(numbers is [0 or 1, <= 2, >= 3]); // False
}

03、模式匹配应用

上面的各种模式匹配主要就用在 is 运算符switch 语句switch 表达式 中。

image.png

3.1、is运算符

is 运算符 本来主要是用来检测类型兼容性的,加上模式匹配就能玩出各种花样了,极大简化了让各种检查类的代码。

object value = 12;
if (value is int && value is not null) //is类型检测+逻辑模式
{
Console.WriteLine(value);
}
if (value is int a && a > 6) //+申明模式
{
Console.WriteLine(a);
}
if (value is int age and > 10 and < 14) //关系模式
{
Console.WriteLine(age);
}
var user = new { Name = “sam”, Age = 12 };
if (user is { Name: _, Age: > 10 }) //属性模式
{
Console.WriteLine(user.Name);
}
int[] arr = new int[] { 1, 2, 3 };
if (arr is [> 0, ..]) //列表模式:第一个元素>0
{
Console.WriteLine(arr);
}
var dt = new Tuple<string, int>(“sam”, 100);
if (dt is (_, > 60) d) //位置模式+申明模式(好像没什么用)
{
Console.WriteLine(d.Item1);
}

3.2、switch..case语句

switch..case 语句 是很多语言中都有的基本多条件分支语句,传统的 case 只能用于匹配常量,多用于枚举。

  • case不能穿透,一个case 执行完后必须break结束,或者return返回(退出方法),可以多个case匹配执行一组逻辑代码。
  • 传统的case就是常量模式,而现代的case可以结合上面多种模式使用,非常强大。
  • when,自由附加更多条件。
int age = 22;
string sex = “Male”;
switch (age)
{
case 1:
case 2:
Console.WriteLine(“婴儿”);
break;
case <= 3:
Console.WriteLine(“幼儿”);
break;
case > 10 and < 16:
Console.WriteLine(“青少年”);
break;
case > 18 when sex == “Male”:
Console.WriteLine(“成年男性”);
break;
case int:
break;
}

3.3、switch表达式

C#8switch有了新的语法 —— switch 表达式 ,可以看做是switch..case语句的一个变种,使用比较类似。switch表达式是一个赋值(输出)语句。

  • =>左侧为模式(返回一个bool),如果模式匹配(true)则返回右侧的值,最后一个弃元模式匹配其他情况,同default效果。
int type = 6;
var message = type switch
{
<= 1 => “success”,
2 => “warning”,
3 => “error”,
> 3 and < 10 => “other error”,
_ => “unkonwn error”,
};

可以用when来进行更多的判断,when后面的表达式就很自由了,只要返回boo即可。

object type = 6;
var message = type switch
{
int i when i<6 => “ok”,
string s when s==“null”=>“Null”,
string s when !string.IsNullOrEmpty(s)=>“string value”,
_=>“unknown value”
};
Console.WriteLine(message);

支持多个变量的组合模式:用括号()包含多个变量

string gender = “male”;
int age = 10;
string type = (gender,age) switch{
(“male”,>18)=>“VIP”,
(not “male”,>26 and <35)=>“VVIP”,
_=>“”,
};

参考资料


©️版权申明:版权所有@安木夕,本文内容仅供学习,欢迎指正、交流,转载请注明出处!原文编辑地址-语雀

使用c#强大的表达式树实现对象的深克隆 - a1010 - 博客园

mikel阅读(283)

来源: 使用c#强大的表达式树实现对象的深克隆 – a1010 – 博客园

一、表达式树的基本概念

表达式树是一个以树状结构表示的表达式,其中每个节点都代表表达式的一部分。例如,一个算术表达式 a + b 可以被表示为一个树,其中根节点是加法运算符,它的两个子节点分别是 a 和 b。在 LINQ(语言集成查询)中,表达式树使得能够将 C# 中的查询转换成其他形式的查询,比如 SQL 查询。这样,同样的查询逻辑可以用于不同类型的数据源,如数据库、XML 文件等。由于表达式树可以在运行时创建和修改,同样的它们非常适合需要根据运行时数据动态生成或改变代码逻辑的场景。这对于需要重复执行的逻辑(比如本文提到的深克隆)是非常有用的,因为它们可以被优化和缓存,从而提高效率。

二、创建和使用表达式树

C# 中,我们可以通过 System.Linq.Expressions 命名空间中的类来创建和操作表达式树。以下是一个创建简单表达式树的示例:

复制代码
        // 创建一个表达式树表示 a + b
        ParameterExpression a = Expression.Parameter(typeof(int), "a");
        ParameterExpression b = Expression.Parameter(typeof(int), "b");
        BinaryExpression body = Expression.Add(a, b);

        // 编译表达式树为可执行代码
        var add = Expression.Lambda<Func<int, int, int>>(body, a, b).Compile();

        // 使用表达式
        Console.WriteLine(add(1, 2));  // 输出 3
复制代码

当我们定义了一个类型后,我们可以通过一个匿名委托进行值拷贝来实现深克隆:

复制代码
//自定义类型
public class TestDto
{
    public int Id { get; set; }
    public string Name { get; set; }
}
//匿名委托
Func<TestDto, TestDto> deepCopy = x => new TestDto()
{
    Id = x.Id,
    Name = x.Name
};
//使用它
var a =new TestDto(){//赋值};
var b = deepCopy(a);//实现深克隆
复制代码

那么想要自动化的创建这一匿名委托就会用到表达式树,通过自动化的方式来实现匿名委托的自动化创建,这样就可以实现复杂的自动化表达式创建从而不必依赖反射、序列化/反序列化等等比较消耗性能的方式来实现。核心的业务逻辑部分如下:首先我们需要知道表达式树通过反射来遍历对象的属性,来实现x = old.x这样的赋值操作。而对于不同的属性比如数组、字典、值类型、自定义类、字符串,其赋值方案是不同的,简单的值类型和字符串我们可以直接通过=赋值,因为这两者的赋值都是“深”克隆。也就是赋值后的变量修改不会影响原始变量。而复杂的字典、数组、对象如果使用=赋值,则只会得到对象的引用,所以针对不同的情况需要不同的处理。

首先我们需要定义一个接口ICloneHandler,针对不同情况使用继承该接口的处理类来处理:

interface ICloneHandler
{
    bool CanHandle(Type type);//是否可以处理当前类型
    Expression CreateCloneExpression(Expression original);//生成针对当前类型的表达式树
}

接着我们定义一个扩展类和扩展函数,用于处理深拷贝:

复制代码
public static class DeepCloneExtension
{
    //创建一个线程安全的缓存字典,复用表达式树
    private static readonly ConcurrentDictionary<Type, Delegate> cloneDelegateCache = new ConcurrentDictionary<Type, Delegate>();
    //定义所有可处理的类型,通过策略模式实现了可扩展
    private static readonly List<ICloneHandler> handlers = new List<ICloneHandler>
    {
       //在此处添加自定义的类型处理器
    };
    /// <summary>
    /// 深克隆函数
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="original"></param>
    /// <returns></returns>
    public static T DeepClone<T>(this T original)
    {
        if (original == null)
            return default;
        // 获取或创建克隆表达式
        var cloneFunc = (Func<T, T>)cloneDelegateCache.GetOrAdd(typeof(T), t => CreateCloneExpression<T>().Compile());
        //调用表达式,返回结果
        return cloneFunc(original);
    }
    /// <summary>
    /// 构建表达式树的主体逻辑
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    private static Expression<Func<T, T>> CreateCloneExpression<T>()
    {
        //反射获取类型
        var type = typeof(T);
        // 创建一个类型为T的参数表达式 'x'
        var parameterExpression = Expression.Parameter(type, "x");
        // 创建一个成员绑定列表,用于稍后存放属性绑定
        var bindings = new List<MemberBinding>();
        // 遍历类型T的所有属性,选择可读写的属性
        foreach (var property in type.GetProperties().Where(prop => prop.CanRead && prop.CanWrite))
        {
            // 获取原始属性值的表达式
            var originalValue = Expression.Property(parameterExpression, property);
            // 初始化一个表达式用于存放可能处理过的属性值
            Expression valueExpression = null;
            // 标记是否已经处理过此属性
            bool handled = false;
            // 遍历所有处理器,查找可以处理当前属性类型的处理器
            foreach (var handler in handlers)
            {
                // 如果找到合适的处理器,使用它来创建克隆表达式
                if (handler.CanHandle(property.PropertyType))
                {
                    valueExpression = handler.CreateCloneExpression(originalValue);
                    handled = true;
                    break;
                }
            }
            // 如果没有找到处理器,则使用原始属性值
            if (!handled)
            {
                valueExpression = originalValue;
            }
            // 创建属性的绑定
            var binding = Expression.Bind(property, valueExpression);
            // 将绑定添加到绑定列表中
            bindings.Add(binding);
        }
        // 使用所有的属性绑定来初始化一个新的T类型的对象
        var memberInitExpression = Expression.MemberInit(Expression.New(type), bindings);
        // 创建并返回一个表达式树,它表示从输入参数 'x' 到新对象的转换
        return Expression.Lambda<Func<T, T>>(memberInitExpression, parameterExpression);
    }
}
复制代码

接下来我们就可以添加一些常见的类型处理器:

数组处理:

复制代码
class ArrayCloneHandler : ICloneHandler
{
    Type elementType;
    public bool CanHandle(Type type)
    {
        //数组类型要特殊处理获取其内部类型
        this.elementType = type.GetElementType();
        return type.IsArray;
    }

    public Expression CreateCloneExpression(Expression original)
    {
        //值类型或字符串,通过值类型数组赋值
        if (elementType.IsValueType || elementType == typeof(string))
        {
            return Expression.Call(GetType().GetMethod(nameof(DuplicateArray), BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(elementType), original);
        }
        //否则使用引用类型赋值
        else
        {
            var arrayCloneMethod = GetType().GetMethod(nameof(CloneArray), BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(elementType);
            return Expression.Call(arrayCloneMethod, original);
        }
    }
    //引用类型数组赋值
    static T[] CloneArray<T>(T[] originalArray) where T : class, new()
    {
        if (originalArray == null)
            return null;

        var length = originalArray.Length;
        var clonedArray = new T[length];
        for (int i = 0; i < length; i++)
        {
            clonedArray[i] = DeepClone(originalArray[i]);//调用该类型的深克隆表达式
        }
        return clonedArray;
    }
    //值类型数组赋值
    static T[] DuplicateArray<T>(T[] originalArray)
    {
        if (originalArray == null)
            return null;

        T[] clonedArray = new T[originalArray.Length];
        Array.Copy(originalArray, clonedArray, originalArray.Length);
        return clonedArray;
    }
}
复制代码

自定义类型处理(其实就是调用该类型的深克隆):

复制代码
class ClassCloneHandler : ICloneHandler
{
    Type elementType;
    public bool CanHandle(Type type)
    {
        this.elementType = type;
        return type.IsClass && type != typeof(string);
    }

    public Expression CreateCloneExpression(Expression original)
    {
        var deepCloneMethod = typeof(DeepCloneExtension).GetMethod(nameof(DeepClone), BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(elementType);
        return Expression.Call(deepCloneMethod, original);
    }
}
复制代码

接着我们就可以在之前的DeepCloneExtension中添加这些handles

复制代码
private static readonly List<ICloneHandler> handlers = new List<ICloneHandler>
{
    new ArrayCloneHandler(),//数组
    new DictionaryCloneHandler(),//字典
    new ClassCloneHandler()//类
    ...
};
复制代码

最后我们可以通过简单的进行调用就可以实现深克隆了

var a = new TestDto() { Id = 1, Name = "小明", Child = new TestDto() { Id = 2, Name = "小红" }, Record = new Dictionary<string, int>() { { "1年级", 1 }, { "2年级", 2 } }, Scores = [100, 95] };
var b = a.DeepClone();

总之,C# 的表达式树提供了一个强大的机制,可以将代码以数据结构的形式表示出来,使得代码可以在运行时进行检查、修改或执行。这为动态查询生成、代码优化和动态编程提供了很多可能性。

.NET有哪些好用的定时任务调度框架 - 追逐时光者 - 博客园

mikel阅读(255)

来源: .NET有哪些好用的定时任务调度框架 – 追逐时光者 – 博客园

前言

定时任务调度的相关业务在日常工作开发中是一个十分常见的需求,经常有小伙伴们在技术群提问:有什么好用的定时任务调度框架推荐的?今天大姚给大家分享5个.NET开源、简单、易用、免费的任务调度框架,帮助大家在做定时任务调度框架技术选型的时候有一个参考。

以下开源任务调度收录地址:https://github.com/YSGStudyHards/DotNetGuide/blob/main/docs/DotNet/DotNetProjectPicks.md

Quartz.Net

一款功能齐全的任务调度系统,从小型应用到大型企业级系统都能适用。功能齐全体现在触发器的多样性上面,即支持简单的定时器,也支持Cron表达式;即能执行重复的作业任务,也支持指定例外的日历;任务也可以是多样性的,只要继承IJob接口即可。

Quartz.NET是一个功能齐全的开源作业调度系统,可用于从最小的应用程序到大规模企业系统。

Quartz.NetUI

Quartz.NetUI是一个基于.NET Core + Quartz.NET + Vue + IView开箱即用的定时任务UI(不依赖数据库,只需在界面做简单配置)。

Hangfire

Hangfire 是一个非常简单易用的库,可以在 .NET 应用程序中执行后台的、延迟的和定期的任务。它支持 CPU 密集型、I/O 密集型、长时间运行和短时间运行的任务。无需使用 Windows 服务或任务计划程序。同时,Hangfire 可以与 Redis、SQL Server、SQL Azure 和 MSMQ 集成。

Hangfire.HttpJob

Hangfire.HttpJob是一个基于Hangfire开发的一个组件、一个调度器(调度服务),这个调度器启动后你可以在管理面板上添加作业(让你通过web请求的方式去调度你的作业)。

FreeScheduler

FreeScheduler 是利用 IdleBus 实现的轻量化定时任务调度,支持临时的延时任务和重复循环任务(可持久化),可按秒,每天/每周/每月固定时间,自定义间隔执行,支持 .NET Core 2.1+、.NET Framework 4.0+ 运行环境。

DotNetGuide技术社区交流群

  • DotNetGuide技术社区是一个面向.NET开发者的开源技术社区,旨在为开发者们提供全面的C#/.NET/.NET Core相关学习资料、技术分享和咨询、项目框架推荐、求职和招聘资讯、以及解决问题的平台。
  • 在DotNetGuide技术社区中,开发者们可以分享自己的技术文章、项目经验、学习心得、遇到的疑难技术问题以及解决方案,并且还有机会结识志同道合的开发者。
  • 我们致力于构建一个积极向上、和谐友善的.NET技术交流平台。无论您是初学者还是有丰富经验的开发者,我们都希望能为您提供更多的价值和成长机会。