Tuple VS ValueTuple

为什么有此文章

首先要说的是我们公司内部技术框架是用 abp.vnext 框架整合而来的,我们架构师对于 abp 相关的知识都很了然于胸了。并且这个框架的确很优秀,省了我们前期大量基础工作。架构师把主要的架子搭建好了之后,把应用层与核心层让我们去实现,并让我们好熟悉这个框架。

就在我们在讨论代码规范相关的东西,就说到了值元祖这个点,并提议不要在代码中用元组。我当时听了之后觉得疑惑,为什么不能用元组呢?元组的确很方便啊,特别是 C#7.0 之后支持元组解构,代码阅读性,美观度双双提升。他是说元组在取值的时候会发生装箱,会有性能损耗。再者值元组跟之前的 Tuple 不同,前者是一个结构体,后者则是引用类型,在用值元组的时候会不利于垃圾回收(具体是说 Ioc 管理的生命周期与我在用的值元组变量的生命周期会有矛盾)。

在最开始的话,我并没有这么考虑,因为我心里想着是这样的:

  1. Tuple<T> 和 ValueTuple<T> 是泛型的,是不会发生装箱的(这点在我查看了源代码以及 IL 发现很有意思,后面会有提到)

  2. ValueTuple<T> 是值对象没错,内存分配在栈中,但还是属于托管资源,CLR 会管理好每个变量的生命周期的,会确保值类型变量在当前作用域无任何引用时会释放资源。比如我在程序中是新建的局部变量,那么哪怕是这个变量未引用,已经引用过后再无引用,CLR 都会自动回收这个局部变量。

而后我查看 Tuple 和 ValueTuple 的api,心情可谓是一波三折啊。所以在有此文章

ValueTuple

先来看 ValueTuple,查看其成员信息如下:

public struct ValueTuple : IStructuralComparable, IStructuralEquatable, IComparable, IComparable<ValueTuple>, IEquatable<ValueTuple>, ITuple

这里面有一个成员信息特别扎眼,那就是 ITuple 类,因为其他的接口都是跟判断相等性相关的。不在我们这次的讨论范围。

我们 F12 到 ITuple 进去看看具体成员信息

object? this[int? index] { get; }int? Length { get; }

不过我们在暴露出来的 API 中没有看到这两个实现属性,说明这个实现类的这这两个属性只是内部实现用的,不会给我们开发者用(当然,我们可以选择强转来使用接口的这两个属性)。这从源码也是可以很容易知道的:

[Nullable(2)]
object ITuple.this[int index]
{get{if(index != 0){throw new IndexOutOfRangeException();}return Item1;}
}
int ITuple.Length
{get{return 1;}
}

当我在官网看到 ValueTuple 有两个属性是实现接口 ITuple 的,并且 ITuple.Item[Int32] 返回的是一个 object 对象,我下意识的反映就是难道真的会发生装箱么?仔细想想其实完全不是这样,如果是发生装箱的话,那么这个 ValueTuple 泛型就是一个多余的东西,那就跟 java 中的泛型擦除效果一样了,只是起到了一个编译期的检测作用,不能做到实质的性能提升。

其实再仔细查看便会发现,我们平常引用的 ValueTuple 、Tuple 实例对象引用的 Item1、Item2 等值实际上是字段而不是属性,而这些字段在你初始化或用 Tuple.Creat,ValueTuple.Create 函数创建的元组 / 值元组对象时,类型以及 Item 的个数以及值就已经确定了。所以根本不会发生装箱。这一点我们从 IL 代码中就能从中得知

在看 IL 之前我们先来看与 IL 对应的 C# 代码

var t = ValueTuple.Create(2, 3);
Console.WriteLine(t.Item1);
Console.WriteLine(t.Item2);
Console.WriteLine($"Item1 = {t.Item1}, Item2= ${t.Item2}");

IL 代码:

.method private hidebysig static void Main (string[] args) cil managed 
{.maxstack 3.entrypoint.locals init ([0] valuetype [System.Runtime]System.ValueTuple`2<int32, int32> t)IL_0000: nopIL_0001: ldc.i4.2IL_0002: ldc.i4.3IL_0003: call valuetype [System.Runtime]System.ValueTuple`2<!!0, !!1> [System.Runtime]System.ValueTuple::Create<int32, int32>(!!0, !!1)IL_0008: stloc.0IL_0009: ldloc.0IL_000a: ldfld !0 valuetype [System.Runtime]System.ValueTuple`2<int32, int32>::Item1IL_000f: call void [System.Console]System.Console::WriteLine(int32)IL_0014: nopIL_0015: ldloc.0IL_0016: ldfld !1 valuetype [System.Runtime]System.ValueTuple`2<int32, int32>::Item2IL_001b: call void [System.Console]System.Console::WriteLine(int32)IL_0020: nopIL_0021: ldstr "Item1 = {0}, Item2= ${1}"IL_0026: ldloc.0IL_0027: ldfld !0 valuetype [System.Runtime]System.ValueTuple`2<int32, int32>::Item1IL_002c: box [System.Runtime]System.Int32IL_0031: ldloc.0IL_0032: ldfld !1 valuetype [System.Runtime]System.ValueTuple`2<int32, int32>::Item2IL_0037: box [System.Runtime]System.Int32IL_003c: call string [System.Runtime]System.String::Format(string, object, object)IL_0041: call void [System.Console]System.Console::WriteLine(string)IL_0046: nopIL_0047: ret
}

这样我们就能很清楚的知道元组里面的细节了,我们平常取的都是元组 / 值元组的字段,并且 Main 函数开头的 managed 标识就代表这是托管资源。值得注意的是 IL_002c 处的装箱只是由于 Console.WriteLine 导致的装箱。

元组解构

我们知道 C#7 支持了元组结构了,可以支持我们对元组字段 Item 进行表意话,这样更能提高阅读性和代码美观。那么元组结构跟之前直接引用的字段值变量 Item 有什么区别呢?这一点我们也可以直接从 IL 上轻易得知。

var (pd, id) = ValueTuples.Create(2, 3);
Console.WriteLine(pd);
Console.WriteLine(id);
Console.WriteLine($"元组解构:Item1 = {pd}, Item2= ${id}");.method private hidebysig static void Main (string[] args) cil managed 
{.maxstack 3.entrypoint.locals init ([0] int32 pd,[1] int32 id)IL_0000: nopIL_0001: ldc.i4.2IL_0002: ldc.i4.3IL_0003: call valuetype [System.Runtime]System.ValueTuple`2<!!0, !!0> CSharpGuide.LanguageVersions._7._0.ValueTuples::Create<int32>(!!0, !!0)IL_0008: dupIL_0009: ldfld !0 valuetype [System.Runtime]System.ValueTuple`2<int32, int32>::Item1IL_000e: stloc.0IL_000f: ldfld !1 valuetype [System.Runtime]System.ValueTuple`2<int32, int32>::Item2IL_0014: stloc.1IL_0015: ldloc.0IL_0016: call void [System.Console]System.Console::WriteLine(int32)IL_001b: nopIL_001c: ldloc.1IL_001d: call void [System.Console]System.Console::WriteLine(int32)IL_0022: nopIL_0023: ldstr "元组解构:Item1 = {0}, Item2= ${1}"IL_0028: ldloc.0IL_0029: box [System.Runtime]System.Int32IL_002e: ldloc.1IL_002f: box [System.Runtime]System.Int32IL_0034: call string [System.Runtime]System.String::Format(string, object, object)IL_0039: call void [System.Console]System.Console::WriteLine(string)IL_003e: nopIL_003f: ret
} 

发现了没有,这段 IL 与之前的一模一样,没任何区别。

ITuple

如果你想用把元组转换成 ITuple 类型,那么取的值就一定会发生装箱,因为 Item 是一个 object 类型。我们能从这个类获知这个元组有多少个值,能通过索引遍历所有的值。除此之外,这个类并没有其他使用场景了。

TupleValueTuple 平常的使用完全不用担心 Item 值的装箱,因为根本不会发生装箱拆箱。元组解构生成的代码跟之前直接引用元组是没任何区别的。只是编译器增加这么一个功能,给 item 命名的功能而已。如果你想要遍历这个元组对象的值的话,那么就建议转化成 ITuple 进一步操作。

文章同步至:https://github.com/MarsonShine/Books/blob/master/CSharpGuide/docs/7.0/TupleVSValueTuple.md

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313530.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于.NET HttpClient方式获取微信小程序码(二维码

随着微信小程序的火热应用&#xff0c;市面上有关小程序开发的需求也多了起来。近来分析了一项生成有关生成微信小程序码的需求——要求扫码跳转到小程序指定页面&#xff08;带参数&#xff09;&#xff1b;看了下小程序官方文档&#xff0c;以及网上的例子&#xff0c;未看到…

从零开始使用Skywalking分布式链路追踪系统

当我们用很多服务时&#xff0c;各个服务间的调用关系是怎么样的&#xff1f;各个服务单调用的顺序\时间性能怎么样?服务出错了&#xff0c;到底是哪个服务引起的&#xff1f;这些问题我们用什么方案解决呢&#xff0c;以前的方式是各个系统自己单独做日志&#xff0c;出了问题…

快速排序 (Quick Sort)(Java实现)

快速排序&#xff08;Quicksort&#xff09;是对冒泡排序的一种改进&#xff0c;借用了分治的思想&#xff0c;由C. A. R. Hoare在1962年提出。 1、基本思想 快速排序的基本思想&#xff1a;挖坑填数分治法。 首先选一个轴值(pivot&#xff0c;也有叫基准的)&#xff0c;通过…

使用.net core3.0 正式版创建Winform程序

前阵子一直期待.net core3.0正式版本的出来&#xff0c;以为这个版本出来&#xff0c;Winform程序又迎来一次新生了&#xff0c;不过9.23日出来的马上下载更新VS&#xff0c;创建新的.net core Winform项目&#xff0c;发现并没有Winform窗体设计器。而微软目前则是通过插件的方…

深入理解 ValueTask

.NET Framework 4 里面的命名空间为 System.Threading.Tasks的 Task 类。这个类以及它派生的 Task<TResult> 早已成为编程的主要部分&#xff0c;在 C#5 中的异步编程模式当作介绍了 async/await。在这篇文章里&#xff0c;我会覆盖新的类 ValueTask / ValueTask<TRes…

谓词筛选表达式的扩展库PredicateLib

PredicateLib是谓词筛选表达式Expression<Func<T, bool>>的一个扩展库&#xff0c;它可以帮你创建一个复杂且灵活的Expression<Func<T, bool>>&#xff0c;以作为EF、MongoDB Driver等ORM框架的查询条件。1 Predicate的创建1.1 true或false Predicatev…

NET Core 3.0 AutoFac替换内置DI的新姿势

.NET Core 3.0 和 以往版本不同&#xff0c;替换AutoFac服务的方式有了一定的变化&#xff0c;在尝试着升级项目的时候出现了一些问题。原来在NET Core 2.1时候&#xff0c;AutoFac返回一个 IServiceProvider 参数注入到ConfigureServices .NET Core 服务中&#xff0c;基本大痣…

XSS(跨站脚本攻击)攻击与防御

一、 XSS攻击原理 XSS原称为CSS(Cross-Site Scripting)&#xff0c;因为和层叠样式表(Cascading Style Sheets)重名&#xff0c;所以改称为XSS(X一般有未知的含义&#xff0c;还有扩展的含义)。XSS攻击涉及到三方&#xff1a;攻击者&#xff0c;用户&#xff0c;web server。用…

asp.net core 使用Mysql和Dapper

序曲&#xff1a;学习编程最好的方式就是敲代码&#xff0c;没有比这个更好的方法&#xff0c;哪怕你看了上百G的视频&#xff0c;都不如你自己敲几行代码更为有效。还有要记得敲完代码然后写一篇随笔来记录一下你所学所想。大家都知道&#xff0c;.netcore是微软一个具有历史意…

CSFR(跨站请求伪造)攻击与防御

一、CSRF是什么&#xff1f; CSRF&#xff08;Cross-site request forgery&#xff09;&#xff0c;中文名称&#xff1a;跨站请求伪造&#xff0c;也被称为&#xff1a;one click attack/session riding&#xff0c;缩写为&#xff1a;CSRF/XSRF。 二、CSRF可以做什么&#…

Vue 3源码公布

10 月 5 日凌晨&#xff0c;Vue.js 框架的作者兼核心开发者尤雨溪公布了尚处于 Pre-Alpha 状态的 Vue 3 源码。说学不动的童鞋抓紧剩余的假期时间撸一遍源码吧 : D作者表示&#xff0c;Vue 3 主要的架构改进、优化和新功能均已完成&#xff0c;剩下的主要任务是完成一些 Vue 2 …

简析web注入攻击

注入攻击是众多攻击之一&#xff0c;在注入攻击中&#xff0c;攻击者给程序提供恶意的输入&#xff0c;解析引擎把恶意输入作为命令或者查询的一部分&#xff0c;顺带着改变了程序执行的流程。 注入是web程序最古老的和最危险的攻击之一&#xff0c;它们能导致数据被偷&#x…

在副业刚需的时代,如何掌握副业的正确姿势?

前言近期&#xff0c;伴随着“副业刚需”这个词语的流行&#xff0c;关于“职场人要不要发展副业”的话题再一次被炒得沸沸扬扬。有人认为副业是刚需&#xff0c;是抵御中年危机的锦囊妙计&#xff0c;甚至是中年人该有的自觉&#xff0c;没有副业的人不足以谈人生&#xff0c;…

使用.NET Core创建Windows服务(二) - 使用Topshelf方式

原文&#xff1a;Creating Windows Services In .NET Core – Part 2 – The “Topshelf” Way作者&#xff1a;Dotnet Core Tutorials译者&#xff1a;Lamond Lu译文&#xff1a;使用.NET Core创建Windows服务&#xff08;二&#xff09; - 使用Topshelf方式使用.NET Core创建…

常用加密算法(Java实现)总结

1、Java的安全体系架构 Java中为安全框架提供类和接口。JDK 安全 API 是 Java 编程语言的核心 API&#xff0c;位于 java.security 包&#xff08;及其子包&#xff09;&#xff0c;以及sun.securityAPI包&#xff08;及其子包&#xff09;中。设计用于帮助开发人员在程序中同…

怎样的项目才能称为“成功项目”?

作者&#xff1a;邹溪源&#xff0c;长沙资深互联网从业者&#xff0c;架构师社区合伙人&#xff01;引子这个故事讲的是一家拥有百年历史的制造业大厂的信息化转型过程中的波折。这家企业拥有超过三万名员工&#xff0c;它是某行业的领先品牌&#xff0c;但是在信息化程度上却…

彩虹表

一、简介 彩虹表就是一个庞大的、针对各种可能的字母组合预先计算好的哈希值的集合&#xff0c;不一定是针对MD5算法的&#xff0c;各种算法的都有&#xff0c;有了它可以快速的破解各类密码。越是复杂的密码&#xff0c;需要的彩虹表就越大&#xff0c;现在主流的彩虹表都是1…

深入Dapper.NET源码

经过业界前辈、StackOverflow多年推广,「Dapper搭配Entity Framework」成为一种功能强大的组合,它满足「安全、方便、高效、好维护」需求。但目前中文网路文章,虽然有很多关于Dapper的文章但都停留在如何使用,没人系统性解说底层原理。所以有了此篇「深入Dapper源码」想带大家进…

DDoS攻击与防御

一、DDOS介绍 要了解DDOS攻击是什么&#xff0c;首先要了解DOS攻击的基本原理是至关重要的。 DoS攻击是最早出现的&#xff0c;它的攻击方法说白了就是单挑&#xff0c;是比谁的机器性能好、速度快。但是现在的科技飞速发展&#xff0c;一般的网站主机都有十几台主机&#xf…

DRDoS(memcache漏洞导致的反射型分布式拒绝服务攻击)

一、DDoS基础 见博文&#xff1a;DDoS攻击与防御 二、Memcached 反射DDOS攻击原理 反射DDOS是发送大量带有被害者IP地址的请求给反射服务器&#xff0c;反射服务器对IP地址源做出大量回应&#xff0c;形成拒绝服务攻击。CLOUDFLARE的这张图很好的解释了DDOS反射攻击过程&…