第壹章第15节 C#和TS语言对比-泛型
C#提供了泛型的完整支持,不仅在编译时,运行时仍然保留泛型的类型信息,同时提供了更加丰富的泛型约束和更加全面的协变逆变支持。TS的泛型,在语法表现形式上,和C#差不多,但本质上两者是不一样的。TS的泛型,和Java一样,使用类型擦除机制,泛型只存在于编译时,在运行时,泛型的类型信息会被移除。
一、泛型究竟有啥意义
1.1 泛型应用的经典案例
正常情况下,我们还没真正懂泛型的时候,已经大量使用到(注意,是使用,不是定义),比如List、Func、Repository<MyClass,long>等等。下面是一个经典例子,大家参考一下,我觉得要到自己去封装一个泛型仓储时,才会真正领会到泛型的强大,这部分内容要到AspNetCore一章。下例中,假设我们需要创建一个数据存储类,它可以存储和检索任何类型的数据,我们使用泛型来实现这个需求。
//1、C#实现==========================================================================
//定义类时,使用泛型(类型的占位符)
public class Storage<T>
{//存储数据的字段_dataprivate T _data;//保存数据的方法public void Store(T data){_data = data;Console.WriteLine("Data stored: " + data);}//获取数据的方法public T Retrieve(){Console.WriteLine("Data retrieved: " + _data);return _data;}
}
//实例化类时,确定泛型的具体类型
public class Program
{public static void Main(){//保存和读取整数类型数据Storage<int> intStorage = new Storage<int>();intStorage.Store(42);int retrievedInt = intStorage.Retrieve();//保存和读取字符串类型数据Storage<string> stringStorage = new Storage<string>();stringStorage.Store("Hello, World!");string retrievedString = stringStorage.Retrieve();}
}//2、TS实现==========================================================================
class Storage<T> {//存储数据的字段_dataprivate data: T;//保存数据的方法store(data: T): void {this.data = data;console.log("Data stored: " + data);}//读取数据的方法retrieve(): T {console.log("Data retrieved: " + this.data);return this.data;}
}
//保存和读取整数类型数据
const intStorage = new Storage<number>();
intStorage.store(42);
const retrievedInt = intStorage.retrieve();
//保存和读取字符串类型数据
const stringStorage = new Storage<string>();
stringStorage.store("Hello, World!");
const retrievedString = stringStorage.retrieve();
1.2 泛型的作用总结
- 上例中我们进行了整数和字符串的保存和读取,实际上可以进行任意类型数据的保存和读取。如果没有泛型,我们需要定义StorageInt、StorageString等等超多个class,如果方法逻辑变了,还得一个个修改。减少代码重复、增强类型安全,这是泛型的最大意义,即使像TS、Java这样的类型擦除机制的泛型,都能实现。
- 实际开发中,除了大量使用到框架为我们定义好的泛型类型,比如语言框架提供的List、Array、Action、Func等,也有系统框架为提供的,比如ABP的Repository、Vue的defineProps()等等。我们自己也时常会定义自己的泛型,特别是在有数据传输场景,由于无法提前获知数据的具体类型,通常需要封装一个通用方法,比如使用TS开发前端应用时,一般需要使用泛型二次封装一下axios。
- 有人说,泛型是类型的参数,非常对,但不好理解。我觉得,说泛型是类型的占位符,会更易理解。比如上例中的_data字段,我们定义时,无法确定具体类型,那就先用一个符号代替,用的时候,再确定具体类型。这个占位符可以是本例的T,也可以是V,也可以是TValue,它只是一个占位的符号。
- 补充说一下类型擦除:Java和TS的泛型,都使用了类型擦除机制,泛型只存在于编译时,运行时泛型会被移除。这会造成什么影响呢?举个例子,由于泛型类型被移除,List和List,在运行时,通过反射获取类型时,结果是一样的,无法区分,这会在一定程度上限制反射的能力,同时也增加了一些不可预测的运行时类型错误的风险。总之,C#的泛型肯定是可以将Java按在地上摩擦的,除此之后,还有许多语言特性,一样有此能力,比如反射、LINQ、属性、事件,async/await…
二、泛型类、接口和方法
2.1 泛型类的定义和使用
2.1.1 C#中定义和使用泛型类
//定义泛型类
public class GenericClass<T>
{private T _value; //字段类型public GenericClass(T value) //构造方法的参数{_value = value;}public T GetValue() //方法返回值{return _value;}public void SetValue(T value) //方法参数{_value = value;}
}
//使用泛型类
GenericClass<int> intInstance = new GenericClass<int>(10);
int value = intInstance.GetValue(); // 10
2.1.1 TS中定义和使用泛型类
//定义泛型类
class GenericClass<T> {private value: T;constructor(value: T) {this.value = value;}getValue(): T {return this.value;}setValue(value: T): void {this.value = value;}
}
//使用泛型类
let intInstance = new GenericClass<number>(10);
let value = intInstance.getValue(); // 10
2.2 泛型方法的定义和使用
2.2.1 C#中定义和使用泛型方法
//定义泛型方法
public class GenericMethods
{public T GenericMethod<T>(T parameter){return parameter;}
}
//使用泛型方法
GenericMethods gm = new GenericMethods();
int intValue = gm.GenericMethod<int>(5); // 5
string stringValue = gm.GenericMethod<string>("hello"); // hello
2.2.2 TS中定义和使用泛型方法
//定义泛型方法
class GenericMethods {genericMethod<T>(parameter: T): T {return parameter;}
}
//使用泛型方法
let gm = new GenericMethods();
let intValue = gm.genericMethod<number>(5); // 5
let stringValue = gm.genericMethod<string>("hello"); // hello
2.3 泛型接口的定义和使用
2.3.1 C#中定义和使用泛型接口
//定义泛型接口=======================================================================
public interface IGenericInterface<T>
{T GetValue();void SetValue(T value);
}//实现泛型接口时,类依然是泛型类,接口的类型依然没有确定===============================
//类的泛型T和接口泛型T是对应着的
public class GenericClass<T> : IGenericInterface<T>
{private T _value;public GenericClass(T value){_value = value;}public T GetValue(){return _value;}public void SetValue(T value){_value = value;}
}//实现泛型接口时,并确定接口具体的类型================================================
public class SpecificClass : IGenericInterface<string>
{private string _value;public GenericClass(string value){_value = value;}public string GetValue(){return _value;}public void SetValue(string value){_value = value;}
}//实现多个泛型接口===================================================================
public class MultipleRepository<T> : IRepository<T>, IAnotherRepository<T>{...}
2.3.2 TS中定义和使用泛型接口
//定义泛型接口=======================================================================
interface IGenericInterface<T> {getValue(): T;setValue(value: T): void;
}//实现泛型接口时,类依然是泛型类,接口的类型依然没有确定===============================
//类的泛型T和接口泛型T是对应着的
class GenericClass<T> implements IGenericInterface<T> {private value: T;constructor(value: T) {this.value = value;}getValue(): T {return this.value;}setValue(value: T): void {this.value = value;}
}//和C#一样,实现泛型接口时,也可以确定接口的类型======================================
public class SpecificClass implements IGenericInterface<string>{...}//实现多个泛型接口===================================================================
class MultipleRepository<T> implements IRepository<T>, IAnotherRepository<T> {...}
2.4 泛型约束
2.4.1 C#中的泛型约束
//1、T必须是一个值类型,包括结构和枚举================================================
public class StructConstraint<T> where T : struct
{public T Value { get; set; }
}//2、T必须是一个引用类型,包括类、接口、委托或数组=====================================
public class ClassConstraint<T> where T : class
{public T Value { get; set; }
}//3、T必须有一个无参构造函数==========================================================
public class NewConstraint<T> where T : new()
{public T CreateInstance(){return new T();}
}//4、T必须是指定基类的派生类==========================================================
public class BaseClass {}
public class BaseClassConstraint<T> where T : BaseClass
{public T Value { get; set; }
}//5、T必须实现指定的接口=============================================================
public interface IMyInterface {}
public class InterfaceConstraint<T> where T : IMyInterface
{public T Value { get; set; }
}//7、多重约束=======================================================================
public class MultipleConstraints<T> where T : class, IMyInterface, new()
{public T CreateInstance(){return new T();}
}
2.4.2 TS中的泛型约束
TS的类型约束和C#差异比较大,更加灵活。差异原因,主要是它们的类型系统不一样。
//1、类型约束========================================================================
//T必须包含指定字面量对象、接口或类的属性(类型兼容)
//1.1 通过字面量对象,传入的参数必须包含length属性-----------------------
function logLength<T extends { length: number }>(arg: T): void {console.log(arg.length);
}
logLength("hello"); // 输出: 5
logLength([1, 2, 3]); // 输出: 3
//调用logLength时,省略了泛型,等价于:logLength<string>("hello")//1.2 通过接口来实现---------------------------------------------------
interface Lengthwise {length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {console.log(arg.length);return arg;
}
loggingIdentity({ length: 10, value: 3 });//1.3 通过类来实现-----------------------------------------------------
//只要类型能够兼容就可以,详见多态,不举例了//2、联合类型约束====================================================================
//T必须是多个类型之一
function combine<T extends number | string>(a: T, b: T): T {if (typeof a === 'number' && typeof b === 'number') {return (a + b) as T;}if (typeof a === 'string' && typeof b === 'string') {return (a + b) as T;}throw new Error("Invalid arguments");
}
console.log(combine(1, 2)); // 输出: 3
console.log(combine("hello", " world")); // 输出: "hello world"//3、带有索引签名的约束==============================================================
//keyof接收一个对象类型,生成其键名的联合类型
//下列中,keyof obj,生成的是"a"|"b"|"c"
function getProperty<T, K extends keyof T>(obj: T, key: K) {return obj[key];
}
let obj = { a: 1, b: 2, c: 3 };
console.log(getProperty(obj, "a")); // 输出: 1
2.5 泛型默认值(C#)和默认类型(TS)
2.5.1 C#中的泛型【默认值】
使用default关键字来为泛型类型参数提供默认值。default关键字根据类型参数是值类型还是引用类型,返回相应的默认值。值类型,返回值类型的默认值,如int为0,bool为false;引用类型返回null
public class GenericClass<T>
{private T _value;public GenericClass(){_value = default(T); // 使用default关键字设置默认值}public T GetValue(){return _value;}}public class Program
{public static void Main(){// 对于值类型GenericClass<int> intInstance = new GenericClass<int>();Console.WriteLine(intInstance.GetValue()); // 输出: 0// 对于引用类型GenericClass<string> stringInstance = new GenericClass<string>();Console.WriteLine((stringInstance.GetValue() ?? "null")); // 输出:null}
}
2.5.2 TS中的泛型【默认类型】
TS没有默认值的概念,但有一个默认类型。
class GenericClass<T = string> {private value: T;constructor(value: T) {this.value = value;}getValue(): T {return this.value;}setValue(value: T): void {this.value = value;}
}// 使用默认类型
let defaultInstance = new GenericClass("Hello");
console.log(defaultInstance.getValue()); // 输出: Hello// 显式提供类型参数
let numberInstance = new GenericClass<number>(42);
console.log(numberInstance.getValue()); // 输出: 42
三、泛型的协变和逆变
C#中的协变和逆变是两个非常抽象的概念,先硬记住下面两个用法,至于如何定义,试过两次,没绕出来,放弃了,反正实际开发过程中,从来没有自己定义过。本质上,它是泛型的多态,可以实现泛型类型之间的类型兼容。而TS的类型兼容是鸭子类型,灵活很多,不需要协变和逆变的特性。以下案例为C#中,协变和逆变的使用,不涉及定义环节。由于TS类型兼容的特性,下面代码稍微改下,放到TS中,也是成立的。
//协变,允许将泛型类型参数的子类型分配给泛型类型参数。泛型集合用到======================
IEnumerable<string> strings = new List<string> { "Hello", "World" };
IEnumerable<object> objects = strings; // 协变
foreach (var obj in objects)
{Console.WriteLine(obj); // 输出: Hello World
}//逆变,允许将泛型类型参数的基类型分配给泛型类型参数。泛型委托用到======================
Action<object> actObject = obj => Console.WriteLine(obj);
Action<string> actString = actObject; // 逆变
actString("Hello, World!"); // 输出: Hello, World!
相关文章:

第壹章第15节 C#和TS语言对比-泛型
C#提供了泛型的完整支持,不仅在编译时,运行时仍然保留泛型的类型信息,同时提供了更加丰富的泛型约束和更加全面的协变逆变支持。TS的泛型,在语法表现形式上,和C#差不多,但本质上两者是不一样的。TS的泛型&a…...

苹果电脑下载vite包错
苹果电脑下载vite包错/Users/lili/.npm/_cacache/index-v5/c5/50/b451703d03b3802b9ee6b7ff2b0bde4de7f26830eb52c904d6911c137cf8包错解决方式 解决方式:sudo chown -R 501:20 "/Users/wangxin/.npm"...

自动化测试git的使用
git是一款分布式的配置管理工具。本文主要讲git如何在自动化测试中安装,上传及拉取下载代码。 1 、git 介绍 每天早上到公司,从公司的git服务器上下载最新的代码,白天在最新的代码基础上,编写新的代码,下班时把“代码…...

MyBatis系列四: 动态SQL
动态SQL语句-更复杂的查询业务需求 官方文档基本介绍案例演示if标签应用实例where标签应用实例choose/when/otherwise应用实例foreach标签应用实例trim标签应用实例[使用较少]set标签应用实例[重点]课后练习 上一讲, 我们学习的是 MyBatis系列三: 原生的API与配置文件详解 现在…...

Jenkins构建 Maven项目(微服务)并自动发布
前面讲了docker 安装Jenkins和gitlab代码管理工具,接下来我们讲一下Jenkins怎么构建 Maven项目。 1. 首先Jenkins配置下面3中工具类 首先是在本地安装三个jenkins自动配置相关的工具 1.1 JDK 由于我们使用docker来启动jenkins,其自带有jdk,…...

简单易用的多功能图床Picsur
什么是 Picsur ? Picsur 是一款易于使用、可自行托管的图片分享服务,类似于 Imgur,并内置转换功能。支持多种格式的图片,包括 QOI、JPG、PNG、WEBP(支持动画)、TIFF、BMP、GIF(支持动画…...

数据库-查询语句习题
SELECT Sname 姓 名,year of birth: 出生年,YEAR(GETDATE())-Sage BIRTHYEAR,LOWER(SNAME) SNAME --起别名 没有特殊字符不需要引号,有特殊字符要加引号;别名(解释作用显示给用户看)用空格或as连接 FROM STUDENT; --消除重复行 DI…...

进程间通信以及线程的同步互斥机制
1.进程间通信机制 常用的六种通信机制: 管道、消息队列、共享内存、信号灯集、信号、Socket 管道(Pipe)和无名管道(匿名管道): 管道是一种半双工的通信方式,数据只能单向流动,通常…...

优思学院|做车企的质量工程师转行跳槽能干嘛?
前言 质量工程师,是现代制造业和服务业中不可或缺的重要角色。他们负责制定和执行提高产品质量和优化业务流程的战略。这不仅涉及设立质量标准、开发测试系统,还包括记录生产过程中的问题并找到解决方案。尤其在汽车行业,由于对质量的高度要…...

ctfshow-web入门-命令执行(web53-web55)
目录 1、web53 2、web54 3、web55 1、web53 这里的代码有点不一样,说一下这两种的区别: (1)直接执行 system($c); system($c);这种方式会直接执行命令 $c 并将命令的输出直接发送到标准输出(通常是浏览器ÿ…...

【INTEL(ALTERA)】make: nios2-swexample-create:未找到命令
目录 说明 解决方法 说明 由于外部内存接口英特尔 Stratix 10 FPGA IP 出现问题,如果在 Windows 平台上使用英特尔 Quartus Prime Pro Edition Software v20.4 或更早版本的"使用软Nios处理器进行片上调试"选项,编译Nios II 片上处理器调试…...

一周刷爆leetcode!(b站视频)
文章目录 一、排序思想的题目二、使用步骤1. 一、排序思想的题目 跟着b站一周刷爆leetcode这个视频开始刷一下leetcode的题目 进行一下记录啥的 二、使用步骤 1. 315. 计算右侧小于当前元素的个数 代码如下: 写了一下暴力解法,没有通过 使用归并排序…...

1.xshell传不了文件输出0000如何解决.....2.k8s中metalLB文件内容
xshell传不了文件输出0000如何解决 centos版本 1,因为没有工具下载即可 yum -y install lrzszk8s中metalLB文件内容 2.metalLB文件内容 cat metallb-native.yaml apiVersion: v1 kind: Namespace metadata:labels:pod-security.kubernetes.io/audit: privilegedpod-securit…...

01- ES6语法
1.ES6相关概念 1.1 什么是ES6 1.1.1 简介 ES6, 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准,2015.06 发版。 ES6 主要是为了解决 ES5 的先天不足,比如 JavaScript 里并没有类的概念,但是目前浏览器的 Ja…...

STM32MP135裸机编程:配置RCC,修改主频到1GHz
0 工具准备 STM32CubeMX v6.11.1 STM32CubeIDE v1.15 STM32CubeProgrammer v2.16.0 STM32MP13xx参考手册 STM32MP13勘误手册 STM32MP135AD数据手册 正点原子stm32MP135开发板 1 确认时钟源 本例使用的时钟源均由外部晶振提供,分别是24MHz的HSE、32.768KHz的LSE。原…...

观察 jvm 运行时数据区内存大小(native memory tracking)
jvm 运行时数据区 jvm 运行时数据区包括且不限于以下几个部分: 堆(heap): 用于存储对象实例和数组。堆内存的分配和释放由垃圾回收器进行管理。方法区(method area): 用于存储类的信息、静态变量、常量等。jdk 8 后方法区位于 metaspace。虚拟机栈(vm stack): 用于存储方法的…...

【论文阅读】-- 时间空间化:用于深度分类器训练的可扩展且可靠的时间旅行可视化
Temporality Spatialization: A Scalable and Faithful Time-Travelling Visualization for Deep Classifier Training 摘要1 引言2 动机3 问题定义4 方法论4.1 时空复合体4.2 复数约简 5 实验6 相关工作7 结论参考文献 摘要 时间旅行可视化回答了深度分类器的预测是如何在训练…...

Windows系统部署本地SQL_Server指引
Windows系统部署本地SQL_Server指引 此指引文档环境为Windows10系统,部署SQL_Server 2019为例,同系列系统软件安装步骤类似。 一、部署前准备; 下载好相关镜像文件;设备系统启动后,将不必要的软件停用,避…...

Aptos Builder Jam 亚洲首站|议程公布,无限畅想 Aptos 生态未来
作为一个新兴的 Layer1 公链,Aptos 自诞生之日起的理想便是 “A Layer 1 for everyone” 当 Web3 深陷熊市阴影之时,Aptos 奋力为开发者找到了全新的技术路径,正有 200 项目正在开发,并且已有大量 DeFi 项目落实部署工作ÿ…...

Vue3使用component动态展示组件
前言: 最近在研究gitHub中的一个项目并将与自己之前完成的项目进行结合,其中有一个功能就是需要使用根据不同的字段,渲染不同的组件,查阅资料发现可以使用component完成这个功能,在实现的过程中也会遇见一些坑&#x…...

嵌入式中间件_2.嵌入式中间件的分类
1.中间件的分类 中间件的范围十分广泛,针对不同的应用需求涌现出了多种各具特色的中间件产品。因此,在不同的角度或不同的层次上,对中间件的分类也会有所不同。 根据IDC在1998年对中间件进行的分类,把中间件分为终端仿真/屏幕转换…...

论文精读——KAN
目录 1.研究背景 2.关键技术 2.1 原始公式 2.2 KAN结构 2.3 缩放定律 3.技术扩展 4.模型效果 5.相关讨论 6.总结 文章标题:《KAN: Kolmogorov–Arnold Networks》 文章地址: KAN: Kolmogorov-Arnold Networks (arxiv.org)https://arxiv.org/a…...

全国产城市轨道交通运营公安AI高清视频监控系统
方案简介 城市轨道交通运营公安高清视频监控系统解决方案针对运营部门和公安部门的安保需求,选用华维视讯的各类前端和视频编解码、控制产品,通过统一平台提供视频监控服务和智能应用,满足轨道交通运营业主客运组织和抢险指挥的需求ÿ…...

python连接mysql数据库、FastAPI、mysql-connector-python
方法工具一、FastAPI 建议使用fast api中的pymysql pip3 install fastapi pip3 install pydantic pip3 install "uvicorn[standard]" pip3 中的3是 Python 3 版本的包管理器命令,用于安装和管理 Python 3 版本的第三方库。在某些系统中,同时…...

【idea】解决springboot项目中遇到的问题
一、Maven报错Could not find artifact com.mysql:mysql-connector-j:pom:unknown in aliyunmaven解决及分析 报错 创建springboot项目,勾选数据库驱动,springboot版本为3,现在改成了2.7.2,Maven就发生了报错Could not find art…...

ubuntu22.04禁止自动休眠的几种方式
在Ubuntu 20.04中,您可以通过以下几种方式禁用自动休眠功能: 使用systemd设置: sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target 修改/etc/systemd/logind.conf文件: sudo nano /etc/systemd/logind.conf 找…...

智能网站管理系统
智能网站管理系统,即智能化的网站管理工具,是为了提高网站管理效率和简化操作流程而开发的一种软件系统。它集合了各种先进的技术和功能,为网站管理员提供了一套强大而可靠的解决方案。 智能网站管理系统的核心功能是网站内容管理。传统的网站…...

Android Service学习笔记
1、Service介绍 Android Service(服务)是 Android 四大组件之一,主要作用是执行后台操作。它是一个后台运行的组件,执行长时间运行且不需要用户交互的任务。即使应用被销毁也依然可以工作。 Service并不是运行在一个独立的进程当…...

amr文件怎么转换成mp3?超好用的四种转换方法介绍!
amr文件怎么转换成mp3?在当今数字化时代,音频格式的多样性给我们带来了更广泛的选择,其中AMR格式就是其中之一,AMR格式在录音和通话领域得到广泛应用,但与此同时,它也存在一些挑战和局限性,尽管…...

翻转数位00
题目链接 翻转数位 题目描述 注意点 可以将一个数位从0变为1找出能够获得的最长的一串1的长度(必须是连续的) 解答思路 参照题解使用动态规划解决本题,对于任意一个位置i,dp[i][0]表示到达且包含第i位不翻转0最长1的长度&…...