一:背景

1. 讲故事

前几天在写一个api接口,需要对衣物表进行分页查询,查询的output需要返回两个信息,一个是 totalCount,一个是 clothesList,在以前我可能需要封装一个 PagedClothes 类,如下代码:

public class PagedClothes{public int TotalCount { get; set; }public List<Clothes> ClothesList { get; set; }}static PagedClothes GetPageList(){return new PagedClothes(){TotalCount = 100,ClothesList = new List<Clothes>() { }};}

在 C# 7.0 之后如果觉得封装一个类太麻烦或者没这个必要,可以用快餐写法,如下代码:

static (int, List<Clothes>) GetPageList(){return (10, new List<Clothes>() { });}

这里的 (int, List<Clothes>)  是什么意思呢?懂的朋友看到 (x,y) 马上就会想到它是 .NET 引入的一个新类:ValueTuple,接下来的问题就是真的是ValueTuple吗?用ILSpy 看看 IL 代码:

.method private hidebysig static valuetype [System.Runtime]System.ValueTuple`2<int32, class [System.Collections]System.Collections.Generic.List`1<class ConsoleApp2.Clothes>> GetPageList () cil managed {IL_0000: nopIL_0001: ldc.i4.s 10IL_0003: newobj instance void class [System.Collections]System.Collections.Generic.List`1<class ConsoleApp2.Clothes>::.ctor()IL_0008: newobj instance void valuetype [System.Runtime]System.ValueTuple`2<int32, class [System.Collections]System.Collections.Generic.List`1<class ConsoleApp2.Clothes>>::.ctor(!0, !1)IL_000d: stloc.0IL_000e: br.s IL_0010IL_0010: ldloc.0IL_0011: ret} // end of method Program::GetPageList

GetPageList 方法的 IL 代码可以很容易的看出方法返回值和return处都有 System.ValueTuple 标记,从此以后你可能就会以为 (x,y) 就是 ValueTuple 的化身 ,是吧,问题就出现在这里,这个经验靠谱吗?

二:(x,y) 真的是 ValueTuple 的化身吗

为了去验证这个经验是否靠谱,我需要举几个例子:

1. (x,y) 接收方法返回值时是 ValueTuple 吗

为了更加清楚的表述,先上代码:

        (int totalCount, List<Clothes> orders) = GetPageList();static (int, List<Clothes>) GetPageList(){return (10, new List<Clothes>() { });}

现在已经知道当 (x,y) 作为方法返回值的时候在IL层面会被化作 ValueTuple,那 (x,y) 作为接受返回值的时候又是什么意思呢?还会和 ValueTuple 有关系吗?为了去验证,可以用反编译能力弱点的 dnspy 去看看反编译后的代码:

从图中可以看出,当用 (x,y) 模式接收的时候,貌似就是实现映射赋值 或者 叫做拆解赋值,是不是有一点模糊,这个 (x,y) 貌似和 ValueTuple 有关系,又貌似和 ValueTuple 没关系,不过没关系,继续看下一个例子。

2. (x,y) 在 foreach 迭代中是 ValueTuple 吗

(x,y) 也可以出现在 foreach 中,相信第一次看到这么玩还是有一点吃惊的,如下代码:

var dict = new Dictionary<int, string>();foreach ((int k, string v) in dict){}

接下来继续用 dnspy 反编译一下:

我去,这回就清晰了,从图中可以看出,我写的 (x,y) 压根就没有 ValueTuple 的影子,说明在这个场景下两者并没有任何关系,也就是说同样是 (x,y) ,放在不同位置具有不同的表现形式,这就很让人琢磨不透了, 可能有些朋友不死心,想看一下 Deconstruct 到底干了什么,知道的朋友应该明白这个新玩法叫做解构方法,继续看代码:

public readonly struct KeyValuePair<TKey, TValue>{private readonly TKey key;private readonly TValue value;public void Deconstruct(out TKey key, out TValue value){key = Key;value = Value;}}

有些抬杠的朋友会发现一个规律,貌似 (x,y) 放在赋值语句的左边都和 ValueTuple 没有任何关系,放在右边可能会有奇迹发生,那到底是不是这样呢?继续硬着头皮举例子呗。

3. (x,y) 放在赋值语句的右边是 ValueTuple 吗

public class Point{public int X { get; set; }public int Y { get; set; }public Point(int x, int y){(X, Y) = (x, y);}}

嘿嘿,看这句: (X, Y) = (x, y) 里的 (x,y) 是 ValueTuple 的化身吗?我猜你肯定是懵逼状态,是吧,亦真亦假,虚虚实实,证据还是继续反编译看呗。

我去,又是和 ValueTuple 一点关系都没有,啥玩意嘛,乱七八糟的,莫名其妙。

三:总结

说 (x,y) 是元组吧,放在 return 中就变成了 ValueTuple ,说 (x,y) 不是元组吧,放在其他处还真就不是的,是不是很疑惑,为了更加形象,在 Point 中再增加一个 Test 方法,对照一下源码和反编译的代码:

//原始的:public class Point{public int X { get; set; }public int Y { get; set; }                public Point(int x, int y){(X, Y) = (x, y);}public (int x, int y) Test(){return (X, Y);}}//反编译的:public class Point{public int X { get; set; }public int Y { get; set; }public Point(int x, int y){this.X = x;this.Y = y;}[return: TupleElementNames(new string[]{"x","y"})]public ValueTuple<int, int> Test(){return new ValueTuple<int, int>(this.X, this.Y);}}

反正我已经恼火了,就这样吧,少用经验推理,多用工具挖一挖,这样才靠谱!

虚虚实实,亦假亦真的 ValueTuple,绝对能眩晕你相关推荐

  1. 真亦假时假亦真,假亦真时真亦假

    真亦假时假亦真,假亦真时真亦假! 到处充满着迷雾,你看不清,就不知道当中有陷阱,一旦踏入陷阱那就意味着你要花更大的代价才有可能脱离,甚至有的是你一辈子也挣脱不了的,所以,我们需要看清本源,透过现象看本 ...

  2. android 沉浸式_【沉浸式体验】投影秀科技与视觉:体验亦真亦幻的超常感受

    沉浸式投影作为一种新型的交互方式 受大众关注 全面覆盖观众视角 展现给参与者带来了亦真亦幻的超常感受 为参与者带来身临其境的体验感 投影秀科技与视觉+美学的结合 让你的活动与众不同 一起体验投影秀的魅 ...

  3. 我与小娜(04):时空变换,亦真亦幻

    我与小娜(04):时空变换,亦真亦幻       2月5日,我赶回南京浦口车站,从车站小件寄存处把"小口袋"领出来.对着"小口袋"拍了一下,小娜从里面把紧紧闭着 ...

  4. 抚仙湖,一个亦梦亦幻的地方,这个五一节,我们骑友′的诗和远方

    云南省玉溪市抚仙湖是中国南方最大的深水型淡水湖泊之一,位于云南省中部,距离昆明市约80公里.抚仙湖的面积约为212平方公里,平均水深约为120米,最大水深可达157米. 抚仙湖最美的景点有很多,其中最 ...

  5. 假作真时真亦假,无为有处有还无

    [鉴赏]    甄士隐梦中所见的这副对联,在第五回"贾宝玉神游太虚境"时也同样看到.两次重出是着意强调,同时也借此点出甄的遭遇和归宿是贾的一生道路的   缩影.    作者用高度概 ...

  6. “假作真时真亦假”:分类器设计中几个常见的评价指标

    方法学评价常用指标 本文主要介绍机器学习中对设计的分类器性能进行评估所常用的几个评价指标:真阳性.假阳性.真阴性.假阴性.准确率和召回率.它们两两之间或仅有一字之差,却可能代表两种完全不同的含义,因此 ...

  7. ISCC2021 真作假时假亦真

    这道题套娃给我套吐了 这题有点烦人 给了个音频文件,我以为是音频隐写,忙活了半天不是; 把这个音频foremost分离一下 会得到一张图片,把图片拉长, 加了下面的QQ号 这个QQ号里面的 留言板 以 ...

  8. 假作真时真亦假——“真实”IP带来的安全隐患

    Author: lake2, http://lake2.0x54.org 让我们看一段 ASP 代码先: Function getIP() Dim strIPAddr as string If Req ...

  9. 牛散NO.3:MACD放之四海 假作真时真亦假

    大宗商品日线"异曲同工夺命勾魂枪" 话说有实战意义的技术在任何资本市场里都能产生出神奇的效果.不能说放之四海皆准,但至少起到触类旁通的"牵强"吧.大宗商品特别是 ...

最新文章

  1. 将Nodelist快速转换为Array数组
  2. 23.2 编写笨的程序
  3. shell 常用命令
  4. python正则匹配_python 正则表达式详解
  5. android 4.2 判断桌面快捷,Android 判断桌面是否快捷方式,不存在则创建
  6. 使用 ESS SDK 快速创建多实例规格伸缩配置
  7. java向另一activity输入_Activity经典实例一:两个Activity传递数据和对象
  8. 9-2:C++多态之纯虚函数和抽象类以及接口继承和实现继承
  9. Twisted中的putChild和getChild
  10. 操作系统已经向SQL Server 返回了错误21
  11. Linux编程(6)_makefile
  12. 将Access数据库导入到SQLite最简单最实用的方法 -转
  13. perl中shift 和unshift 操作
  14. linux学习笔记:磁盘格式化与磁盘检验命令
  15. 2.晶晨A311D-编译Ubuntu/Debian固件
  16. VScode设置为中文版
  17. 极速office(word)如何在方框内打钩
  18. Vue3源码解析04--响应式核心effect
  19. 南大周志华教授入围院士候选!计算机领域共计7人
  20. 区块链 图灵完备是什么

热门文章

  1. python实现批量压缩文件夹
  2. 关于互斥锁,条件变量的内核源码解析
  3. LeetCode Implement Queue using Stacks (数据结构)
  4. 搜索引擎 ElasticSearch 之 步步为营2 【基础概念】
  5. JavaScript自动设置IFrame高度(兼容各主流浏览器)
  6. mongo-rename操作
  7. HTFS.Software.v7.3-ISO 1DVD(传热模拟,最新完全解密版)
  8. 下载: 虾米音乐_您所说的内容:如何组织凌乱的音乐收藏
  9. java 集合读写同步_JAVA多线程学习十六 - 同步集合类的应用
  10. 程序改变了命运,程序生活一天比一天好,对未来也充满了希望