当前位置: 首页 > 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…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

给网站添加live2d看板娘

给网站添加live2d看板娘 参考文献&#xff1a; stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下&#xff0c;文章也主…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...