类型安全与代码复用的C# 泛型
一、引言:泛型 ——C# 编程的神奇钥匙
在 C# 编程的广袤天地里,泛型宛如一把神奇钥匙,能够开启高效、灵活且安全的代码之门🚪。
想象一下,你是一位经验丰富的建筑师,要建造各种各样的房子🏠。如果没有泛型,就好比你每建造一种风格的房子,都得重新设计一套全新的建造方案,从打地基到盖屋顶,事无巨细都得重来一遍,这无疑是个巨大的工程,不仅耗费时间精力,还容易出错。
而有了泛型,情况就大不一样了!它就像是一个万能的建筑蓝图模板📜,你只需要在这个模板上,根据不同房子的需求(如住宅、别墅、写字楼等),填入具体的参数(比如房间数量、面积大小、功能布局等),就能快速搭建出各种类型的房子。
在 C# 中,泛型让我们可以定义出通用的代码结构,无论是处理整数、字符串,还是自定义的复杂数据类型,都能像使用定制化工具一样得心应手,极大地提升了编程效率与代码质量。现在,就让我们一同深入探索 C# 泛型的奇妙世界吧!
二、初窥泛型:概念与魔法初现
究竟什么是泛型呢🧐?在 C# 中,泛型是一种极为强大的机制,它允许我们在定义方法、属性、索引器、委托、事件乃至类型时,先不着急确定具体的数据类型,而是使用类型参数来作为占位符。就好比我们在打造一个万能模具,这个模具在一开始并不限定只能制作某一种特定形状的物品,而是预留了一些可调整的参数,等到真正使用时,再依据需求填入具体的形状信息,从而制作出各种各样的成品。
比如说,我们有一个传统的非泛型方法,用于打印传入参数的值:
public static void PrintIntValue(int number)
{Console.WriteLine($"{number.GetType()}_Value:{number}");
}
public static void PrintStringValue(string str)
{Console.WriteLine($"{str.GetType()}_Value:{str}");
}
可以看到,如果要处理不同的数据类型,如整数、字符串等,就得分别定义不同的方法,代码显得冗长且重复。一旦需要新增一种数据类型,还得再写一个对应的新方法,这无疑给开发和维护带来了极大的不便。
而使用泛型方法,情况就截然不同了:
public static void PrintValue<T>(T parameter)
{Console.WriteLine($"{parameter.GetType()}_Value:{parameter}");
}
在这个泛型方法 PrintValue 中, 就是类型参数,它就像是一个万能插槽,能够接纳各种不同类型的数据。当我们调用这个方法时,编译器会根据传入的实际参数类型,自动推断出 T 所代表的具体类型,从而实现对不同类型数据的通用处理。例如:
PrintValue(123); // 处理整数
PrintValue("Hello, World!"); // 处理字符串
如此一来,代码变得简洁明了,复用性大大增强,无论后续需要处理多少种新的数据类型,都无需再重复编写类似的方法,极大地提高了开发效率。这,就是泛型的初步魅力展现,是不是已经让你感受到它的神奇之处了呢😃?
三、类型安全:守护代码的坚固盾牌
(一)编译时的安检员
泛型最为突出的优势之一,便是其强大的类型安全保障机制。在编译阶段,编译器就如同一位严谨细致的安检员,会对泛型代码中的类型进行严格审查👮。
当我们定义了一个泛型类 GenericClass,并尝试使用 GenericClass 实例化它时,编译器会确保后续所有操作都与 string 类型相符。若不小心传入了一个整数,编译器立马就会发出 “警报”,提示类型不匹配错误,阻止代码进入运行时,避免了潜在的类型错误隐患。
而在非泛型的场景下,就好比一座没有门禁的城池,各种类型的数据可以随意进出。例如,使用传统的 ArrayList 类(非泛型),它可以存储任意类型的对象,因为它内部将所有元素都当作 Object 类型来处理。这看似方便,实则隐藏巨大风险,一旦进行类型转换操作,就可能在运行时引发 InvalidCastException 异常,导致程序崩溃,就像城池突然陷入混乱一般,后果不堪设想。
所以,泛型的类型安全特性就像是给代码加上了一道坚固的防护盾🛡,提前将错误拦截在编译阶段,让程序的稳定性得到极大保障,为后续的顺利运行奠定坚实基础。
(二)案例警示:类型错误的 “灾难现场”
为了更直观地感受泛型在类型安全方面的巨大优势,我们来看一个具体案例。
假设我们有一个非泛型的方法,用于计算一组数字的平均值:
public static double CalculateAverage(ArrayList numbers)
{double sum = 0;foreach (object number in numbers){sum += (double)number; // 此处存在潜在类型转换问题}return sum / numbers.Count;
}
在调用这个方法时:
ArrayList numberList = new ArrayList();
numberList.Add(10);
numberList.Add(20);
numberList.Add("30"); // 不小心混入了一个字符串
double average = CalculateAverage(numberList);
当代码运行到 sum += (double)number; 这一行时,由于 numberList 中混入了字符串 “30”,在强制转换为 double 类型时,就会抛出 InvalidCastException 异常,程序瞬间崩溃,后续流程无法继续执行,就像一辆高速行驶的汽车突然爆胎,陷入失控状态。
而如果使用泛型来实现同样的功能:
public static double CalculateAverage<T>(List<T> numbers) where T : struct, IConvertible
{double sum = 0;foreach (T number in numbers){sum += number.ToDouble(null);}return sum / numbers.Count;
}
调用时:
List<int> numberList = new List<int>();
numberList.Add(10);
numberList.Add(20);
numberList.Add(30);
double average = CalculateAverage(numberList);
由于泛型方法 CalculateAverage 通过 where T : struct, IConvertible 约束了类型参数 T 必须是可转换为数值类型的结构体,编译器在编译阶段就能发现类型不匹配的问题,比如若尝试传入包含字符串的列表,编译就无法通过,从源头上避免了运行时错误的发生,确保程序稳定运行,就像给汽车配备了优质的轮胎和智能的胎压监测系统,提前预警并排除隐患,一路畅行无阻。
四、代码复用:编程路上的万能钥匙
(一)一处编写,多处适用
泛型最为人称道的特性之一,便是其卓越的代码复用能力。它宛如一把万能钥匙,能够打开适配多种数据类型的代码之门🚪。
在实际编程过程中,我们常常会遇到这样的场景:需要对不同的数据类型执行相似的操作。例如,我们可能需要编写方法来处理整数数组、字符串数组以及自定义数据类型的数组,若不使用泛型,就不得不针对每种数据类型分别创建对应的方法,这无疑会导致代码量剧增,且充斥着大量重复逻辑,后期的维护成本也会直线飙升。
而有了泛型,情况则截然不同。我们可以定义一个泛型方法,使其能够处理各种类型的数组。就像下面这个示例:
public static void PrintArray<T>(T[] array)
{foreach (T item in array){Console.WriteLine(item);}
}
在这个方法中, 作为类型参数,让 PrintArray 具备了处理任意类型数组的能力。无论是 int[]、string[] 还是其他自定义类型的数组,都可以统一交给这个方法来处理:
int[] intArray = { 1, 2, 3, 4, 5 };
string[] stringArray = { "Hello", "World", "!" };
PrintArray(intArray);
PrintArray(stringArray);
如此一来,代码变得简洁高效,我们只需编写一次 PrintArray 方法,就能在不同的数据类型场景中重复使用,极大地提升了开发效率,减少了代码冗余,让我们的编程之路更加顺畅🛣。
(二)泛型类与方法:复用的两大法宝
泛型类和泛型方法就像是代码复用的两大法宝,各自发挥着独特的威力。
先来说说泛型类,它就像是一个万能的工具箱🧰,可以根据不同的需求盛装各种类型的工具(数据)。以一个简单的自定义集合类为例,假设我们要创建一个能够存储不同类型元素的列表:
public class MyList<T>
{private T[] items;private int count;public MyList(){items = new T[10]; // 初始容量为 10,可按需调整count = 0;}public void Add(T item){if (count < items.Length){items[count++] = item;}}public T GetItem(int index){if (index >= 0 && index < count){return items[index];}return default(T); // 返回类型 T 的默认值}
}
通过 MyList,我们可以轻松创建存储不同类型数据的列表实例,如 MyList 用于存储整数,MyList 用于存储字符串,无需为每种数据类型单独编写一个列表类,复用性极强。
再看看泛型方法,它更像是一个万能的工具,能够灵活处理各种类型的数据。比如,我们有一个需求是对两个不同类型的变量进行交换操作:
public static void Swap<T>(ref T a, ref T b)
{T temp = a;a = b;b = temp;
}
这个 Swap 方法可以处理任意类型的变量交换,只要传入的两个变量类型相同即可。无论是交换整数、字符串,还是其他复杂对象(前提是对象所属的类支持赋值操作),它都能完美胜任,真正实现了 “一处编写,处处可用”,为代码复用提供了强有力的支持,让我们在编程的海洋中如鱼得水🐟。
五、实战演练:泛型在项目中的高光时刻
纸上得来终觉浅,让我们走进实际项目的 “战场”,看看泛型是如何大显身手的💪。
在一个数据处理项目中,我们需要对来自不同数据源(如数据库、文件、网络接口等)的数据进行清洗、转换与分析。这些数据类型五花八门,有整数、字符串、日期,还有复杂的自定义数据结构。
若未使用泛型,代码可能会陷入 “类型泥沼”。以数据清洗为例,针对整数类型,我们或许会写一个方法来去除异常值:
public static List<int> CleanIntegerData(List<int> data)
{List<int> cleanData = new List<int>();foreach (int value in data){if (value > 0 && value < 100) // 简单的异常值判断,仅作示例{cleanData.Add(value);}}return cleanData;
}
而对于字符串类型,又得另起炉灶:
public static List<string> CleanStringData(List<string> data)
{List<string> cleanData = new List<string>();foreach (string value in data){if (!string.IsNullOrEmpty(value) && value.Length < 50) // 简单的字符串清理条件,仅作示例{cleanData.Add(value);}}return cleanData;
}
随着数据类型的增多,代码量呈指数级增长,维护成本飙升,仿佛陷入了一团乱麻。
但有了泛型,我们就能打造一个 “万能清洗机”:
public static List<T> CleanData<T>(List<T> data, Func<T, bool> validationRule)
{List<T> cleanData = new List<T>();foreach (T value in data){if (validationRule(value)){cleanData.Add(value);}}return cleanData;
}
这里,通过泛型方法 CleanData,并引入一个委托类型的参数 validationRule 来定义不同类型数据的清理规则。使用时,无论是整数、字符串还是其他类型,都能轻松应对:
// 清洗整数数据
List<int> integerData = new List<int> { 10, -5, 50, 120 };
List<int> cleanIntegerData = CleanData(integerData, value => value > 0 && value < 100);// 清洗字符串数据
List<string> stringData = new List<string> { "", "Hello", "VeryLongString", "World" };
List<string> cleanStringData = CleanData(stringData, value =>!string.IsNullOrEmpty(value) && value.Length < 50);
如此一来,代码简洁明了,复用性极高,无论后续遇到多少种新的数据类型,都能从容应对,极大地提升了开发效率,让数据处理流程如丝般顺滑🛤。
再看业务逻辑层,假设我们正在开发一个电商系统,其中有商品、订单、用户等多个实体类,每个实体类都有一系列的增删改查操作。如果不使用泛型,针对每个实体类都得编写重复的数据库访问代码,这无疑是一场噩梦😫。
以查询操作为例,对于商品实体:
public class ProductRepository
{public Product GetProductById(int id){// 数据库连接、查询语句等操作,此处简化示意return new Product(); }
}
对于订单实体:
public class OrderRepository
{public Order GetOrderById(int id){// 类似的数据库操作,重复且易错return new Order(); }
}
有了泛型,我们可以创建一个通用的仓储基类:
public class GenericRepository<T> where T : class
{public T GetById(int id){// 通用的数据库连接、查询逻辑,根据传入的实体类型 T 动态构建查询return default(T); }
}
然后,商品、订单等实体对应的仓储类只需继承这个泛型基类:
public class ProductRepository : GenericRepository<Product> {}
public class OrderRepository : GenericRepository<Order> {}
这样,不仅减少了大量重复代码,还使得代码结构更加清晰,后期维护与扩展也变得轻而易举,为电商系统的稳定运行与快速迭代奠定了坚实基础,让业务逻辑的实现更加高效、可靠🚀。
六、探索进阶:泛型的更多宝藏
在初步领略了泛型的强大魅力之后,让我们进一步探索它的进阶奥秘,这些进阶特性将如同为我们的编程羽翼添上绚丽的彩羽,助力我们在代码的天空飞得更高更远🛫。
泛型接口是泛型家族中的重要一员,它允许我们定义一套通用的行为规范,而不局限于特定的数据类型。例如,我们定义一个泛型接口 IRepository:
public interface IRepository<T>
{void Add(T item);void Delete(T item);T GetById(int id);
}
不同的数据实体类,如 Product、Order、User 等,只需实现这个泛型接口,就能遵循统一的增删查操作规范,代码的一致性与扩展性瞬间提升,就像为不同品牌的手机都配备了统一标准的充电接口,通用性大大增强。
泛型委托则为方法的传递与调用带来了极大的灵活性。想象一下,我们有一个需求是对不同类型的列表进行排序,传统方式可能需要为每种类型编写特定的排序方法,但有了泛型委托,我们可以这样做:
public delegate int Comparison<T>(T x, T y);public static void Sort<T>(T[] array, Comparison<T> comparer)
{// 排序算法实现,依据传入的比较委托进行元素比较与排序
}
通过定义 Comparison 泛型委托,我们可以灵活传入不同类型的比较逻辑,无论是整数的大小比较,还是自定义对象根据特定属性的比较,都能轻松驾驭,代码复用性达到新高度,仿佛拥有了一把万能的排序钥匙,能打开各种数据类型排序的大门。
而 where 子句(约束)更是为泛型的精准应用保驾护航。比如,我们创建一个泛型方法用于计算两个数的乘积:
public static double Multiply<T>(T num1, T num2) where T : struct, IConvertible
{return num1.ToDouble(null) * num2.ToDouble(null);
}
这里的 where T : struct, IConvertible 约束就像一道精准的滤网,确保传入的类型参数 T 必须是可转换为数值的结构体,避免了错误类型数据的传入,让泛型方法在安全的轨道上高效运行,如同为精密仪器配备了精准的过滤器,保障运行的精准无误。
这些进阶特性仅仅是泛型宝藏的冰山一角,深入挖掘下去,你会发现更多的惊喜与强大功能,它们将持续赋能我们的编程之旅,让代码更加优雅、高效、健壮💪。
七、总结:拥抱泛型,编程升级
至此,我们一同深入探索了 C# 泛型这一强大特性,领略了它在类型安全与代码复用方面的卓越风姿💃🕺。
通过泛型,我们为代码披上了坚固的类型安全铠甲,让那些恼人的类型错误在编译阶段就无所遁形;同时,它又为我们递上了代码复用的万能钥匙,开启了高效开发的大门,无论是面对简单的数据处理,还是复杂的项目架构,都能游刃有余。
在未来的编程旅程中,希望大家积极拥抱泛型,将其巧妙运用到每一个合适的角落,持续优化代码,提升软件质量🚀。编程之路漫漫,让我们保持探索精神,不断挖掘 C# 及其他技术的更多宝藏,向着更高的编程巅峰奋勇攀登,书写属于我们的精彩代码篇章✍!a
相关文章:
类型安全与代码复用的C# 泛型
一、引言:泛型 ——C# 编程的神奇钥匙 在 C# 编程的广袤天地里,泛型宛如一把神奇钥匙,能够开启高效、灵活且安全的代码之门🚪。 想象一下,你是一位经验丰富的建筑师,要建造各种各样的房子🏠。…...
卷积神经05-GAN对抗神经网络
卷积神经05-GAN对抗神经网络 使用Python3.9CUDA11.8Pytorch实现一个CNN优化版的对抗神经网络 简单的GAN图片生成 CNN优化后的图片生成 优化模型代码对比 0-核心逻辑脉络 1)Anacanda使用CUDAPytorch2)使用本地MNIST进行手写图片训练3)…...
vscode使用Marscode编程助手
下载 vscode 在插件里下载Marscode编程助手 插件完成 在这里点击安装,点击后这里出现AI编程插件。...
网络分析仪测试S参数
S参数的测试 一:S参数的定义 S参数(Scattering Parameters,散射参数)是一个表征器件在射频信号激励下的电气行为的工具,它以输入信号、输出信号为元素的矩阵来表现DUT的“传输”和“散射”效应,输入、输出…...
docker mysql5.7如何设置不区分大小写
环境 docker部署,镜像是5.7,操作系统是centos 操作方式 mysql 配置文件是放在 /etc/mysql/mysql.conf.d/mysqld.cnf, vim /etc/mysql/mysql.conf.d/mysqld.cnf lower_case_table_names1 重启mysql容器 验证 SHOW VARIABLES LIKE low…...
【1】Word:邀请函
目录 题目 文字解析 流程 题目 文字解析 考生文件夹☞Word.docx☞一定要用ms打开,wps打开作答无效☞作答完毕,F12或者手动另存为(考生文件夹:路径文件名) 注意:一定要检查,很有可能你前面步…...
【gin】中间件使用之jwt身份认证和Cors跨域,go案例
Gin-3 中间件编程及 JWT 身份认证 1. Gin 中间件概述 中间件是处理 HTTP 请求的函数,可以在请求到达路由处理函数之前或之后对请求进行处理。 在 Gin 框架中,中间件常用于处理日志记录、身份验证、权限控制等功能。 router : gin.Default() router.Us…...
【JAVA实战】@FeignClient注解类通用请求封装
背景 最近在编写多个系统数据集成过程中,经常会使用到FeignClient注解标记一个类,类里面编写很多请求方法,如果第三方系统有非常多的URL请求,每个方法对应一个URL请求,那么这个类就会非常的庞大,是否有一种…...
[c语言日寄]精英怪:三子棋(tic-tac-toe)3命慢通[附免费源码]
哈喽盆友们,今天带来《c语言》游戏中[三子棋boss]速通教程!我们的目标是一边编写博文,一边快速用c语言实现三子棋游戏。准备好瓜子,我们计时开始! 前期规划 在速通中,我们必须要有清晰的前期规划…...
GORM(Go语言数据交互库)
GORM(Go ORM,即对象关系映射)是Go语言中非常流行且功能强大的数据库交互库。它简化了与关系型数据库的交互过程,提供了丰富的API来处理各种数据库操作。下面将详细介绍GORM的功能、使用方法和一些高级特性。 1. 安装 首先&#…...
Redis主从同步是怎么实现的?
Redis主从同步是怎么实现的? 主从节点建立连接后,从节点会进行判断: 1.如果这是从节点之前没有同步过数据 属于初次复制,会进行全量重同步,那么从节点会向主节点发送PSYNC?-1 命令,请求主节点进行全量重…...
Flutter中Get.snackbar避免重复显示的实现
在pubspec.yaml中引入依赖框架。 #GetX依赖注解get: ^4.6.5创建一个SnackBarManager管理类去管理每个提示框。 import package:get/get.dart; import package:flutter/material.dart;class SnackBarManager {factory SnackBarManager() > instance;static final SnackBarMa…...
[Qt]常用控件介绍-多元素控件-QListWidget、QTableWidget、QQTreeWidget
目录 1.多元素控件介绍 2.ListWidget控件 属性 核心方法 核心信号 细节 Demo:编辑日程 3.TableWidget控件 核心方法 QTableWidgetItem核心信号 QTableWidgetItem核心方法 细节 Demo:编辑学生信息 4.TreeWidget控件 核心方法 核心信号…...
深入Android架构(从线程到AIDL)_32 JNI架构原理_Java与C的对接05
1、EIT造形观点 基于熟悉的EIT造形,很容易理解重要的架构设计决策议题。 前言 2、混合式EIT造形 一般EIT造形是同语言的。也就是<E>、 <I>和<T>都使用同一种语言撰写的,例如上述的Java、 C/C等。于此,将介绍一个EIT造…...
【gRPC】clientPool 客户端连接池简单实现与go案例
什么是 gRPC 客户端连接池? 在 gRPC 中,创建和维护一个到服务器的连接是非常消耗资源的(比如 TCP 连接建立和 TLS 握手)。 而在高并发场景下,如果每次请求都创建新的连接,不仅会导致性能下降,还…...
Android 15应用适配指南:所有应用的行为变更
Android系统版本适配,一直是影响App上架Google Play非常重要的因素。 当前Google Play政策规定 新应用和应用更新 必须以 Android 14(API 级别 34)为目标平台,才能提交到Google Play。现有应用 必须以 Android 13(AP…...
24-25-1-单片机开卷部分习题和评分标准
依据相关规定试卷必须按评分标准进行批改。 给分一定是宽松的,能给分一定给,如有疑问也可以向学院教务办申请查卷。 一部分学生期末成绩由于紧张或其他原因导致分数过低,也是非常非常遗憾的。 个人也是非常抱歉的。 开卷考试 简答题 第一…...
STM32第6章、WWDG
一、简介 WWDG:全称Window watchdog,即窗口看门狗,本质上是一个能产生系统复位信号和提前唤醒中断的计数器。 特性: 是一个递减计数器。 看门狗被激活后, 当递减计数器值从 0x40减到0x3F时会产生复位(即T6位…...
汽车免拆诊断案例 | 2007 款法拉利 599 GTB 车发动机故障灯异常点亮
故障现象 一辆2007款法拉利599 GTB车,搭载6.0 L V12自然吸气发动机(图1),累计行驶里程约为6万km。该车因发动机故障灯异常点亮进厂检修。 图1 发动机的布置 故障诊断 接车后试车,发动机怠速轻微抖动,…...
C语言-数据结构-队列
目录 1.队列的特点 2.队列的实现 2.1.初始化队列 2.2.入队列 2.2.1.入空队列 2.2.2.入非空队列 2.3.出队列 2.4.销毁队列 2.5.完整代码 3.实际应用 1.队列的特点 队列是一种常见的数据结构,它遵循先进先出(FIFO, First In First Out)…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
