当前位置: 首页 > news >正文

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

苹果电脑下载vite包错

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

自动化测试git的使用

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

MyBatis系列四: 动态SQL

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

Jenkins构建 Maven项目(微服务)并自动发布

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

简单易用的多功能图床Picsur

什么是 Picsur &#xff1f; Picsur 是一款易于使用、可自行托管的图片分享服务&#xff0c;类似于 Imgur&#xff0c;并内置转换功能。支持多种格式的图片&#xff0c;包括 QOI、JPG、PNG、WEBP&#xff08;支持动画&#xff09;、TIFF、BMP、GIF&#xff08;支持动画&#xf…...

数据库-查询语句习题

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

进程间通信以及线程的同步互斥机制

1.进程间通信机制 常用的六种通信机制&#xff1a; 管道、消息队列、共享内存、信号灯集、信号、Socket 管道&#xff08;Pipe&#xff09;和无名管道&#xff08;匿名管道&#xff09;&#xff1a; 管道是一种半双工的通信方式&#xff0c;数据只能单向流动&#xff0c;通常…...

优思学院|做车企的质量工程师转行跳槽能干嘛?

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

ctfshow-web入门-命令执行(web53-web55)

目录 1、web53 2、web54 3、web55 1、web53 这里的代码有点不一样&#xff0c;说一下这两种的区别&#xff1a; &#xff08;1&#xff09;直接执行 system($c); system($c);这种方式会直接执行命令 $c 并将命令的输出直接发送到标准输出&#xff08;通常是浏览器&#xff…...

【INTEL(ALTERA)】make: nios2-swexample-create:未找到命令

目录 说明 解决方法 说明 由于外部内存接口英特尔 Stratix 10 FPGA IP 出现问题&#xff0c;如果在 Windows 平台上使用英特尔 Quartus Prime Pro Edition Software v20.4 或更早版本的"使用软Nios处理器进行片上调试"选项&#xff0c;编译Nios II 片上处理器调试…...

一周刷爆leetcode!(b站视频)

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

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&#xff0c; 全称 ECMAScript 6.0 &#xff0c;是 JavaScript 的下一个版本标准&#xff0c;2015.06 发版。 ES6 主要是为了解决 ES5 的先天不足&#xff0c;比如 JavaScript 里并没有类的概念&#xff0c;但是目前浏览器的 Ja…...

STM32MP135裸机编程:配置RCC,修改主频到1GHz

0 工具准备 STM32CubeMX v6.11.1 STM32CubeIDE v1.15 STM32CubeProgrammer v2.16.0 STM32MP13xx参考手册 STM32MP13勘误手册 STM32MP135AD数据手册 正点原子stm32MP135开发板 1 确认时钟源 本例使用的时钟源均由外部晶振提供&#xff0c;分别是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系统&#xff0c;部署SQL_Server 2019为例&#xff0c;同系列系统软件安装步骤类似。 一、部署前准备&#xff1b; 下载好相关镜像文件&#xff1b;设备系统启动后&#xff0c;将不必要的软件停用&#xff0c;避…...

Aptos Builder Jam 亚洲首站|议程公布,无限畅想 Aptos 生态未来

作为一个新兴的 Layer1 公链&#xff0c;Aptos 自诞生之日起的理想便是 “A Layer 1 for everyone” 当 Web3 深陷熊市阴影之时&#xff0c;Aptos 奋力为开发者找到了全新的技术路径&#xff0c;正有 200 项目正在开发&#xff0c;并且已有大量 DeFi 项目落实部署工作&#xff…...

Vue3使用component动态展示组件

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

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...