鸿蒙开发案例:推箱子
推箱子游戏(Sokoban)的实现。游戏由多个单元格组成,每个单元格可以是透明的、墙或可移动的区域。游戏使用Cell类定义单元格的状态,如类型(透明、墙、可移动区域)、圆角大小及坐标偏移。而MyPosition类则用于表示位置信息,并提供设置位置的方法。
游戏主体结构Sokoban定义了游戏的基本元素,包括网格单元格的状态、胜利位置、箱子的位置以及玩家的位置等,并提供了初始化游戏状态的方法。游戏中还包含有动画效果,当玩家尝试移动时,会检查目标位置是否允许移动,并根据情况决定是否需要移动箱子。此外,游戏支持触摸输入,并在完成一次移动后检查是否所有箱子都在目标位置上,如果是,则游戏胜利,并显示一个对话框展示游戏用时。
【算法分析】
1. 移动玩家和箱子算法分析:
算法思路:根据玩家的滑动方向,计算新的位置坐标,然后检查新位置的合法性,包括是否超出边界、是否是墙等情况。如果新位置是箱子,则需要进一步判断箱子后面的位置是否为空,以确定是否可以推动箱子。
实现逻辑:通过定义方向对象和计算新位置坐标的方式,简化了移动操作的逻辑。在移动过程中,需要考虑动画效果的控制,以提升用户体验。
movePlayer(direction: string) {const directions: object = Object({'right': Object({ dx: 0, dy: 1}),'left': Object({ dx:0 , dy:-1 }),'down': Object({ dx: 1, dy: 0 }),'up': Object({ dx: -1, dy: 0 })});const dx: number = directions[direction]['dx']; //{ dx, dy }const dy: number = directions[direction]['dy']; //{ dx, dy }const newX: number = this.playerPosition.x + dx;const newY: number = this.playerPosition.y + dy;// 检查新位置是否合法// 箱子移动逻辑...// 动画效果控制...
}
2. 胜利条件判断算法分析:
算法思路:遍历所有箱子的位置,检查每个箱子是否在一个胜利位置上,如果所有箱子都在胜利位置上,则判定游戏胜利。
实现逻辑:通过嵌套循环和数组方法,实现了对胜利条件的判断。这种算法适合用于检查游戏胜利条件是否满足的场景。
isVictoryConditionMet(): boolean {return this.cratePositions.every(crate => {return this.victoryPositions.some(victory => crate.x === victory.x && crate.y === victory.y);});
}
3. 动画控制算法分析:
算法思路:利用动画函数实现移动过程中的动画效果,包括移动过程的持续时间和结束后的处理逻辑。
实现逻辑:通过嵌套调用动画函数,实现了移动过程中的动画效果控制。这种方式可以使移动过程更加流畅和生动。
animateToImmediately({duration: 150,onFinish: () => {animateToImmediately({duration: 0,onFinish: () => {// 动画结束后的处理...}}, () => {// 动画过程中的处理...});}
}, () => {// 动画效果控制...
});
4. 触摸操作和手势识别算法分析:
算法思路:监听触摸事件和手势事件,识别玩家的滑动方向,然后调用相应的移动函数处理玩家和箱子的移动。
实现逻辑:通过手势识别和事件监听,实现了玩家在屏幕上滑动操作的识别和响应。这种方式可以使玩家通过触摸操作来控制游戏的进行。
gesture(SwipeGesture({ direction: SwipeDirection.All }).onAction((_event: GestureEvent) => {// 手势识别和处理逻辑...})
)
【完整代码】
import { promptAction } from '@kit.ArkUI' // 导入ArkUI工具包中的提示操作模块
@ObservedV2 // 观察者模式装饰器
class Cell { // 定义游戏中的单元格类@Trace // 跟踪装饰器,标记属性以被跟踪type: number = 0; // 单元格类型,0:透明,1:墙,2:可移动区域@Trace topLeft: number = 0; // 左上角圆角大小@Trace topRight: number = 0; // 右上角圆角大小@Trace bottomLeft: number = 0; // 左下角圆角大小@Trace bottomRight: number = 0; // 右下角圆角大小@Trace x: number = 0; // 单元格的X坐标偏移量@Trace y: number = 0; // 单元格的Y坐标偏移量constructor(cellType: number) { // 构造函数this.type = cellType; // 初始化单元格类型}
}
@ObservedV2 // 观察者模式装饰器
class MyPosition { // 定义位置类@Trace // 跟踪装饰器,标记属性以被跟踪x: number = 0; // X坐标@Trace y: number = 0; // Y坐标setPosition(x: number, y: number) { // 设置位置的方法this.x = x; // 更新X坐标this.y = y; // 更新Y坐标}
}
@Entry // 入口装饰器
@Component // 组件装饰器
struct Sokoban { // 定义游戏主结构cellWidth: number = 100; // 单元格宽度@State grid: Cell[][] = [ // 游戏网格状态[new Cell(0), new Cell(1), new Cell(1), new Cell(1), new Cell(1), new Cell(1)],[new Cell(1), new Cell(1), new Cell(2), new Cell(2), new Cell(2), new Cell(1)],[new Cell(1), new Cell(2), new Cell(2), new Cell(2), new Cell(1), new Cell(1)],[new Cell(1), new Cell(2), new Cell(2), new Cell(2), new Cell(2), new Cell(1)],[new Cell(1), new Cell(1), new Cell(2), new Cell(2), new Cell(2), new Cell(1)],[new Cell(0), new Cell(1), new Cell(1), new Cell(1), new Cell(1), new Cell(1)],];@State victoryPositions: MyPosition[] = [new MyPosition(), new MyPosition()]; // 胜利位置数组@State cratePositions: MyPosition[] = [new MyPosition(), new MyPosition()]; // 箱子位置数组playerPosition: MyPosition = new MyPosition(); // 玩家位置@State screenStartX: number = 0; // 触摸开始时的屏幕X坐标@State screenStartY: number = 0; // 触摸开始时的屏幕Y坐标@State lastScreenX: number = 0; // 触摸结束时的屏幕X坐标@State lastScreenY: number = 0; // 触摸结束时的屏幕Y坐标@State startTime: number = 0; // 游戏开始时间isAnimationRunning: boolean = false // 动画是否正在运行aboutToAppear(): void { // 游戏加载前的准备工作// 初始化某些单元格的圆角大小...this.grid[0][1].topLeft = 25;this.grid[0][5].topRight = 25;this.grid[1][0].topLeft = 25;this.grid[4][0].bottomLeft = 25;this.grid[5][1].bottomLeft = 25;this.grid[5][5].bottomRight = 25;this.grid[1][1].bottomRight = 10;this.grid[4][1].topRight = 10;this.grid[2][4].topLeft = 10;this.grid[2][4].bottomLeft = 10;this.initializeGame(); // 初始化游戏}initializeGame() { // 初始化游戏状态this.startTime = Date.now(); // 设置游戏开始时间为当前时间// 设置胜利位置和箱子位置...this.startTime = Date.now(); // 设置游戏开始时间为当前时间this.victoryPositions[0].setPosition(1, 3);this.victoryPositions[1].setPosition(1, 4);this.cratePositions[0].setPosition(2, 2);this.cratePositions[1].setPosition(2, 3);this.playerPosition.setPosition(1, 2);}isVictoryPositionVisible(x: number, y: number): boolean { // 判断位置是否为胜利位置return this.victoryPositions.some(position => position.x === x && position.y === y); // 返回是否有胜利位置与给定位置匹配}isCratePositionVisible(x: number, y: number): boolean { // 判断位置是否为箱子位置return this.cratePositions.some(position => position.x === x && position.y === y); // 返回是否有箱子位置与给定位置匹配}isPlayerPositionVisible(x: number, y: number): boolean { // 判断位置是否为玩家位置return this.playerPosition.x === x && this.playerPosition.y === y; // 返回玩家位置是否与给定位置相同}movePlayer(direction: string) {const directions: object = Object({'right': Object({ dx: 0, dy: 1}),'left': Object({ dx:0 , dy:-1 }),'down': Object({ dx: 1, dy: 0 }),'up': Object({ dx: -1, dy: 0 })});const dx: number = directions[direction]['dx']; //{ dx, dy }const dy: number = directions[direction]['dy']; //{ dx, dy }const newX: number = this.playerPosition.x + dx;const newY: number = this.playerPosition.y + dy;const targetCell = this.grid[newX][newY];// 检查新位置是否超出边界if (!targetCell) {return;}// 如果新位置是墙,则不能移动if (targetCell.type === 1) {return;}let crateIndex = -1;if (this.isCratePositionVisible(newX, newY)) {const crateBehindCell = this.grid[newX + dx][newY + dy];if (!crateBehindCell || crateBehindCell.type !== 2) {return;}crateIndex = this.cratePositions.findIndex(crate => crate.x === newX && crate.y === newY);if (crateIndex === -1 || this.isCratePositionVisible(newX + dx, newY + dy)) {return;}}if (this.isAnimationRunning) {return}this.isAnimationRunning = trueanimateToImmediately({duration: 150,onFinish: () => {animateToImmediately({duration: 0,onFinish: () => {this.isAnimationRunning = false}}, () => {if (crateIndex !== -1) {this.grid[this.cratePositions[crateIndex].x][this.cratePositions[crateIndex].y].x = 0;this.grid[this.cratePositions[crateIndex].x][this.cratePositions[crateIndex].y].y = 0;this.cratePositions[crateIndex].x += dx;this.cratePositions[crateIndex].y += dy;}this.grid[this.playerPosition.x][this.playerPosition.y].x = 0this.grid[this.playerPosition.x][this.playerPosition.y].y = 0this.playerPosition.setPosition(newX, newY);// 检查是否获胜const isAllCrateOnTarget = this.cratePositions.every(crate => {return this.victoryPositions.some(victory => crate.x === victory.x && crate.y === victory.y);});if (isAllCrateOnTarget) {console.log("恭喜你,你赢了!");// 可以在这里添加胜利处理逻辑promptAction.showDialog({// 显示对话框title: '游戏胜利!', // 对话框标题message: '恭喜你,用时:' + ((Date.now() - this.startTime) / 1000).toFixed(3) + '秒', // 对话框消息buttons: [{ text: '重新开始', color: '#ffa500' }] // 对话框按钮}).then(() => { // 对话框关闭后执行this.initializeGame(); // 重新开始游戏});}})}}, () => {this.grid[this.playerPosition.x][this.playerPosition.y].x = dy * this.cellWidth;this.grid[this.playerPosition.x][this.playerPosition.y].y = dx * this.cellWidth;if (crateIndex !== -1) {this.grid[this.cratePositions[crateIndex].x][this.cratePositions[crateIndex].y].x = dy * this.cellWidth;this.grid[this.cratePositions[crateIndex].x][this.cratePositions[crateIndex].y].y = dx * this.cellWidth;}console.info(`dx:${dx},dy:${dy}`)})}build() {Column({ space: 20 }) {//游戏区Stack() {//非零区加瓷砖Column() {ForEach(this.grid, (row: [], rowIndex: number) => {Row() {ForEach(row, (item: Cell, colIndex: number) => {Stack() {Text().width(`${this.cellWidth}lpx`).height(`${this.cellWidth}lpx`).backgroundColor(item.type == 0 ? Color.Transparent :((rowIndex + colIndex) % 2 == 0 ? "#cfb381" : "#e1ca9f")).borderRadius({topLeft: item.topLeft > 10 ? item.topLeft : 0,topRight: item.topRight > 10 ? item.topRight : 0,bottomLeft: item.bottomLeft > 10 ? item.bottomLeft : 0,bottomRight: item.bottomRight > 10 ? item.bottomRight : 0})//如果和是胜利坐标,显示叉号Stack() {Text().width(`${this.cellWidth / 2}lpx`).height(`${this.cellWidth / 8}lpx`).backgroundColor(Color.White)Text().width(`${this.cellWidth / 8}lpx`).height(`${this.cellWidth / 2}lpx`).backgroundColor(Color.White)}.rotate({ angle: 45 }).visibility(this.isVictoryPositionVisible(rowIndex, colIndex) ? Visibility.Visible : Visibility.None)}})}})}Column() {ForEach(this.grid, (row: [], rowIndex: number) => {Row() {ForEach(row, (item: Cell, colIndex: number) => {//是否显示箱子Stack() {Text().width(`${this.cellWidth}lpx`).height(`${this.cellWidth}lpx`).backgroundColor(item.type == 1 ? "#412c0f" : Color.Transparent).borderRadius({topLeft: item.topLeft,topRight: item.topRight,bottomLeft: item.bottomLeft,bottomRight: item.bottomRight})Text('箱').fontColor(Color.White).textAlign(TextAlign.Center).fontSize(`${this.cellWidth / 2}lpx`).width(`${this.cellWidth - 5}lpx`).height(`${this.cellWidth - 5}lpx`).backgroundColor("#cb8321")//#995d12 #cb8321.borderRadius(10).visibility(this.isCratePositionVisible(rowIndex, colIndex) ? Visibility.Visible : Visibility.None)Text('我').fontColor(Color.White).textAlign(TextAlign.Center).fontSize(`${this.cellWidth / 2}lpx`).width(`${this.cellWidth - 5}lpx`).height(`${this.cellWidth - 5}lpx`).backgroundColor("#007dfe")//#995d12 #cb8321.borderRadius(10).visibility(this.isPlayerPositionVisible(rowIndex, colIndex) ? Visibility.Visible : Visibility.None)}.width(`${this.cellWidth}lpx`).height(`${this.cellWidth}lpx`).translate({ x: `${item.x}lpx`, y: `${item.y}lpx` })})}})}}Button('重新开始').clickEffect({ level: ClickEffectLevel.MIDDLE }).onClick(() => {this.initializeGame();});}.width('100%').height('100%').backgroundColor("#fdb300").padding({ top: 20 }).onTouch((e) => {if (e.type === TouchType.Down && e.touches.length > 0) { // 触摸开始,记录初始位置this.screenStartX = e.touches[0].x;this.screenStartY = e.touches[0].y;} else if (e.type === TouchType.Up && e.changedTouches.length > 0) { // 当手指抬起时,更新最后的位置this.lastScreenX = e.changedTouches[0].x;this.lastScreenY = e.changedTouches[0].y;}}).gesture(SwipeGesture({ direction: SwipeDirection.All })// 支持方向中 all可以是上下左右.onAction((_event: GestureEvent) => {const swipeX = this.lastScreenX - this.screenStartX;const swipeY = this.lastScreenY - this.screenStartY;// 清除开始位置记录,准备下一次滑动判断this.screenStartX = 0;this.screenStartY = 0;if (Math.abs(swipeX) > Math.abs(swipeY)) {if (swipeX > 0) {// 向右滑动this.movePlayer('right');} else {// 向左滑动this.movePlayer('left');}} else {if (swipeY > 0) {// 向下滑动this.movePlayer('down');} else {// 向上滑动this.movePlayer('up');}}}))}
}
相关文章:

鸿蒙开发案例:推箱子
推箱子游戏(Sokoban)的实现。游戏由多个单元格组成,每个单元格可以是透明的、墙或可移动的区域。游戏使用Cell类定义单元格的状态,如类型(透明、墙、可移动区域)、圆角大小及坐标偏移。而MyPosition类则用于…...

mysql--表的约束
目录 理解表的约束和操作 如何理解? 1、空属性null 2、默认值default 3、列描述comment 4、自动填充zorefill 5、主键primary key (1)创建表时指定可以 (2)创建表后指定key (3)删除主…...

Ubuntu 上安装 docker 并配置 Docker Compose 详细步骤
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storm…...
MySQL去除空白字符(如非标准空格、制表符等)
在 MySQL 中,需要去除 site_name 字段的空格,可以使用 TRIM() 函数。这个函数可以去掉字符串开头和结尾的空格。以下是一个示例查询,演示如何选择去除空格后的 site_name: SELECT TRIM(site_name) AS site_name FROM site_info;如…...
2063:【例1.4】牛吃牧草
【题目描述】 有一个牧场,牧场上的牧草每天都在匀速生长,这片牧场可供15头牛吃20天,或可供20头牛吃10天,那么,这片牧场每天新生的草量可供几头牛吃1天? 【输入】 (无) 【输出】 如题…...

QT开发:深入掌握 QtGui 和 QtWidgets 布局管理:QVBoxLayout、QHBoxLayout 和 QGridLayout 的高级应用
目录 引言 1. QVBoxLayout:垂直布局管理器 基本功能 创建 QVBoxLayout 添加控件 添加控件和设置对齐方式 设置对齐方式 示例代码与详解 2. QHBoxLayout:水平布局管理器 基本功能 创建 QHBoxLayout 添加控件 添加控件和设置对齐方式 设置对齐…...

Bootstrapping、Bagging 和 Boosting
bagging方法如下: bagging和boosting比较...
板块龙头公司
高通 高通(Qualcomm)是一家总部位于美国加利福尼亚州的全球领先半导体和电信设备公司。成立于1985年,高通专注于无线通信技术的研发和创新。 移动处理器: 高通开发的骁龙(Snapdragon)系列芯片广泛用于智能手机和平板电…...

Java项目-基于Springboot的招生管理系统项目(源码+说明).zip
作者:计算机学长阿伟 开发技术:SpringBoot、SSM、Vue、MySQL、ElementUI等,“文末源码”。 开发运行环境 开发语言:Java数据库:MySQL技术:SpringBoot、Vue、Mybaits Plus、ELementUI工具:IDEA/…...

使用 MongoDB 构建 AI:利用实时客户数据优化产品生命周期
在《使用 MongoDB 构建 AI》系列博文中,我们看到越来越多的企业正在利用 AI 技术优化产品研发和用户支持流程。例如,我们介绍了以下案例: Ventecon 的 AI 助手帮助产品经理生成和优化新产品规范 Cognigy 的对话式 AI 帮助企业使用任意语言&a…...

【React】React18核心源码解读
前言 本文使用 React18.2.0 的源码,如果想回退到某一版本执行git checkout tags/v18.2.0即可。如果打开源码发现js文件报ts类型错误请看本人另一篇文章:VsCode查看React源码全是类型报错如何解决。 阅读源码的过程: 下载源码 观察 package…...

部署私有仓库以及docker web ui应用
官方地址:https://hub.docker.com/_/registry/tags 一、拉取registry私有仓库镜像 docker pull registry:latest 二、运⾏容器 docker run -itd -v /home/dockerdata/registry:/var/lib/registry --name "pri_registry1" --restartalways -p 5000:5000 …...

DAY57WEB 攻防-SSRF 服务端请求Gopher 伪协议无回显利用黑白盒挖掘业务功能点
知识点: 1、SSRF-原理-外部资源加载 2、SSRF-利用-伪协议&无回显 3、SSRF-挖掘-业务功能&URL参数 SSRF-原理&挖掘&利用&修复 漏洞原理:SSRF(Server-Side Request Forgery:服务器端请求伪造) ,一种由攻击者构造形成由服务…...
光盘刻录大文件时分卷操作
可以使用 split 命令来将大文件 finetune.tar 分卷为适合光盘大小的文件片段,然后在离线服务器上合并这些分卷文件。以下是具体的操作步骤: 步骤1:分卷文件 假设你的文件 finetune.tar 大小为35GB,并且你想分卷为每个4.7GB&…...
Kafka系列之:生产者性能调优
Kafka系列之:生产者性能调优 一、producer.type二、request.required.acks三、max.request.size四、batch.size五、buffer.memory一、producer.type 在Kafka中,producer.type是一个配置属性,用于指定Producer的类型。它有两个可能的值: sync:同步发送模式。当设置为sync时…...

【linux】进程创建与进程终止
🔥个人主页:Quitecoder 🔥专栏:linux笔记仓 目录 01.进程创建02.进程终止异常终止如何终止exit()_exit() 01.进程创建 #include <unistd.h> pid_t fork(void);返回值:自进程中返回0,父进程返回子进…...

QT的文件操作类 QFile
QFile 是 Qt 框架中用于文件处理的一个类。它提供了读取和写入文件的功能,支持文本和二进制文 件。 QFile 继承自 QIODevice ,因此它可以像其他IO设备一样使用。 主要功能 文件读写: QFile 支持打开文件进行读取或写入操作文件信息&#x…...
java项目篇-用户脱敏展示
用户敏感信息脱敏展示 定义手机号和证件号的 Jackson 自定义序列化器,并在对应需要脱敏的敏感字段上指定自定义序列化器。在进行指定的需要脱敏的字段(身份证号,手机号,银行卡号等)序列化的时候,该字段自动…...
《C++计算引擎:驱动高效计算的强大动力》
在当今数字化时代,高效的计算能力是推动科技进步和创新的关键。而 C作为一种强大的编程语言,在构建高性能计算引擎方面发挥着重要作用。本文将深入探讨 C计算引擎的特点、优势以及在不同领域的应用,带您领略 C在计算领域的独特魅力。 一、C计…...

Linux的hadoop集群部署
1.hadoop是一个分布式系统基础架构,主要解决海量数据额度存储与海量数据的分析计算问题 hdfs提供存储能力,yarn提供资源管理能力,MapReduce提供计算能力 2.安装 一:调整虚拟机内存,4G即可 二:下载安装包 网址:https://mirrors.aliyun.com/apache/hadoop/common/hadoop-3.4.0/…...

利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...