本文要点

  • 应遵循《.NET设计规范:.NET约定惯用法与模式》一书。和十年前第一版出版时一样,书中给出的原则在当前依然有指导意义。
  • API设计是最重要的。设计不好的API会在极大地增加软件缺陷,同时降低可重用性。
  • 时刻牢记“良性循环”(Pit of Success)这一哲理:让正确的事情更易于做,让犯错误更加困难。
  • 移除“线路噪音”(Line Noise)和“样板”(Boilerplate)代码,聚焦于对业务逻辑的关注。
  • 出于性能考虑而牺牲代码清晰度前,请认真考虑一下。

C# 7是一个重大更新,其中提供了很多有意思的新功能。虽然已有大量的文章介绍这些功能可以做什么,但是鲜有文章介绍应如何使用这些功能。本文将过一遍《.NET设计规范:.NET约定惯用法与模式》 (译者注:英文书名为“Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries”)一书中给出的指导原则,力图更好地使用C# 7的新特性。

元组返回(Tuple Returns)

通常在C#编程中,一个函数返回多个值实现起来十分繁琐。一种做法是使用输出参数,这只适用于暴露异步方法的情况。另一种做法是使用 Tuple<T>。创建Tuple<T>过于啰嗦,需要做内存分配,并且Tuple的字段没有描述性名字。也可以使用自定义的结 构体。虽然结构体在性能上要优于元组,但是大量使用一次性类型会将代码弄得一团糟。而使用具有动态特性的匿名类型,存在性能不好的问题,还缺少静态类型检 查。

在C# 7中新提供了元组返回语法,它解决了全部上述问题。下面给出一个基本语法的例子:

public (string, string) LookupName(long id) // tuple return type
{return ("John", "Doe"); //元组常值。
}
var names = LookupName(0);
var firstName = names.Item1;
var lastName = names.Item2;

该函数的实际返回类型是ValueTuple<string, string>。正如名称所示,ValueTuple<string, string>类似于Tuple<T>类,是一个轻量级的结构体。它解决了类型膨胀(Type Bloat)问题,但是依然没有解决描述性名称这一困扰Tuple<T>的问题。我们看一下如下的例子:

public (string First, string Last) LookupName(long id)
var names = LookupName(0);
var firstName = names.First;
var lastName = names.Last;

其中的返回类型依然是ValueTuple<string, string>,但是现在编译器在函数中添加了一个TupleElementNames属性。这样调用该函数的代码就可以使用描述性名称,而不再是Item1或Item2这样的名称了。

警告: TupleElementNames属性只能由编译器赋予。如果返回类型上使用了反射,你将只能看到裸的ValueTuple<T>结构体。因为在获得结果时,属性是位于函数本身上,而这个信息丢失了。

编译器会尽可能维护额外类型的幻象。例如,给出如下这些声明:

var a = LookupName(0);
(string First, string Last) b = LookupName(0);
ValueTuple<string, string> c = LookupName(0);
(string make, string model) d = LookupName(0);

在编译器看来,a和b同是(string First, string Last)。鉴于c被显式声明为ValueTuple<string, string>,因此不存在c.First属性。

该例中d的赋值语句展示了这一设计的失灵之处,即会在一定程度上导致缺失类型安全。字段意外地重命名是一个非常容易发生的问题,一个元组可以错误地 指定给另一个恰好具有同样形状的元组。这同样是由于编译器没有真正地将(string First, string Last)和(string make, string model)区分为不同的类型。

ValueTuple是可变的

有意思的是, ValueTuple是可变的。Mads Torgersen给出了这样的解释:

为什么通常可变结构体是不好的,不要应用于元组?下面给出原因。

如果你按正常的封装方式编写了一个可变结构体,并且其中具有私有的状态,还有公开的修改器(Mutator)属性和方法,那么你可能就会陷入一些严重的错误中。因为只要结构体是保持在只读变量中,那么修改器就会默默地工作于结构体的一个拷贝上!

但是元组的确有公开的可变字段。它在设计上并未考虑修改器,因此不存在出现上述现象的风险。

此外,ValueTuple是结构体,而结构体在传递时需要进行拷贝。结构体并不直接在线程间共享,也不承担“共享可变状态”的风险。这不同于System.Tuple家族的类型,这些类型也是类。为确保线程安全,需要这些类型是不可变的。

注意,这里Torgersen所指的是“字段”,而不是“属性”。对于使用元组返回函数结果的反射库,这会导致问题。

元组返回的指导原则

  • 当字段列表规模较小并不会发生更改时,考虑使用元组返回,而不是out参数。
  • 对元组返回中的描述性名字使用帕斯卡拼写法(PascalCase),这会使得元组字段看上去就像是正常的类和结构中的属性。
  • 在不进行解析就读取元组返回时,使用var,以避免意外地误标字段。
  • X 如果想要对返回值使用反射,应避免返回值元组。
  • X 如果在未来的版本中可能会返回额外的字段,那么就不要在公开API上使用元组返回。在元组返回中添加字段是一种破坏性变更。

----------------------------------------------------节选自infoQ:C# 7编程模式与实践-----------------------------------------------------------------

转载于:https://www.cnblogs.com/zht01/p/6953841.html

C# 7中函数多值返回_转自InfoQ相关推荐

  1. C++中函数模板的返回值是模板类型参数的调用方法

    1 函数模板 模板定义以关键字template开始,后接模板形参表,模板形参表是用尖括号扩住的一个或多个模板形参的列表,形参之间以逗号分隔.关于函数模板的详细介绍,请参考<C++中模板函数及模板 ...

  2. golang函数多值返回示例

    多值返回 函数可以返回任意数量的返回值. swap 函数返回了两个字符串. package mainimport "fmt"func swap(x, y string) (stri ...

  3. python中函数的可变参数_简单谈谈Python中函数的可变参数

    前言 在Python中定义函数,可以用必选参数.默认参数.可变参数和关键字参数,这4种参数都可以一起使用,或者只用其中某些,但是请注意,参数定义的顺序必须是:必选参数.默认参数.可变参数和关键字参数. ...

  4. c++中函数放在等号右边_如何从C或C++中的函数返回多个值?

    新程序员通常在寻找从函数返回多个值的方法.不幸的是,C和C++不允许直接这样做.但是幸运的是,通过一些巧妙的编程,我们可以轻松实现这一目标. 下面是从C函数中返回多个值的方法: 通过使用指针. 通过使 ...

  5. vue 往对象中添加键值对_【Vue】Vue学习之混入

    今天学习了Vue中的"混入"知识点,写篇文章用自己的语言来向自己解释它,如有不足还望指点. 混入(mixins): 混入提供了一种非常灵活的方式,来分发Vue组件中的可复用功能 - ...

  6. 如何动态的向数组中插入键值对_在Java中实现的一个简单“HashMap”

    如何创建Hash表 对于把K(键)-V(值)这样的键值对插入Hash表中,需要执行两个步骤: 1.使用散列函数将K转换为小整数(称为其哈希码). 2.哈希码用于查找索引(hashCode%arrSiz ...

  7. c语言中函数形参值改变了,相应的实参值是否改变,C语言中,如何利用函数和指针变量通过形参变量的值改变实参变量的值...

    必备知识:c语言中的函数调用和指针变量的相关知识 众所周知,函数是C语言中一个十分重要的模块,因为函数的存在,使得C语言的可读性.可维护性.可移植性大大提高.因此,想要学好C语言,必须要学好函数.函数 ...

  8. map中只有一个值 获取_小学数学,为什么一个三角形中最多只有一个直角或一个钝角...

    小学阶段采用的是将任意一个三角形的三个内角,拼接在一起形成一条直线,根据这个事实得出任意三角形的内角和是180度的结论.到初中学了平行线的特性之后,就可以很严谨地证明这个结论.在小学只需要知道这个结论 ...

  9. java中 byte 取值范围_【二进制基础-java中byte的取值范围-推导过程】

    java中用补码表示二进制数,补码的最高位是符号位,最高位为"0"表示正数,最高位为"1"表示负数. 正数补码为其本身: 负数补码为其绝对值各位取反加1: 例如 ...

最新文章

  1. Redis 高级特性(3)—— 持久化及数据恢复
  2. 【2018第五届世界互联网大会】世界互联网领先科技成果发布:带你看看这15项“黑科技”...
  3. 关于fragment之间的数据传输
  4. 真的了解js生成随机数吗
  5. Linux笔记-查询进程,获取其运行时输入的参数
  6. php 5.2.6升级,Centos5.5 简单方法升级php到php5.2.6
  7. stm32F051系列教程 前哨篇 建立一个KEIL工程模板
  8. 死磕18个Java8日期处理,工作必用!收藏起来~
  9. SAP License:销售流程
  10. html5 video视频资源保护,HTML5 视频播放 video
  11. python中flush什么意思,Python的file.flush()到底在做什么?
  12. 【云服务月刊】2018年第7期:云栖大会门票免费送!阿里云MVP招募,就等你了!...
  13. 实现windows和linux互传文件
  14. “人肉搜索”名词解释
  15. Wowza服务器系列(4):使用rtmp协议向wowza推流的wowoza配置方法
  16. Web前端 学习知识点总结(十二)jQuery进阶 表单验证和简单正则表达式
  17. 如何用免费office表格制作课程表
  18. android 网络文件系统,android在手机上的文件系统框架的阐述
  19. 计算机的关闭程序,电脑中取消关机时强制关闭程序提醒的方法
  20. Open For Bussiness (HelloWorld)

热门文章

  1. 【命令init3/5】centos7切换图像界面和dos界面
  2. :架构优化在何时,方成为公司的推动力与核心竞争力
  3. 打造政产学研新型研发机构 加速人工智能科研成果转化
  4. 056_Connect or Sync to your Salesforce database by using an external database
  5. 咱们一起聊聊Zookeeper
  6. Excel vba引用工作表的三种写法
  7. Java Cardioid 心脏形曲线 (整理)
  8. MySQL备份恢复工具xtrabackup
  9. Alibaba Dubbo框架同步调用原理分析-2
  10. 移动Web开发图片自适应两种常见情况解决方案