C# 7.0

本文参考Roslyn项目中的Issue:#118

  1. C# 7.0 新特性1: 基于Tuple的“多”返回值方法

  2. C# 7.0 新特性2: 本地方法

  3. C# 7.0 新特性3: 模式匹配

  4. C# 7.0 新特性4: 返回引用

C#早在最初的发行版C# 1.0中(2002年1月),就借鉴并延续了C/C++中指针参数,原生允许将值类型数据的引用(指针)通过标记ref参数的形式,传递到方法体中。

但对于方法内的值类型引用,该如何以引用的方式返回,却一直以来没有一个非常完美的解决方案,尽管这种用例非常少见。

提一个简单的问题,我们需要获取三个int中的最大值的引用

我们照惯例,回顾下C#7.0之前的做法:

C/C++指针

我们回归到C/C++中,这个问题没有什么好争议的,实现起来会很理所应当的是这样的:

1 int* Max(int* first, int* second, int* third) {
2 int* max = *first > *second ? first : second;
3 return *max > *third ? max : third;
4 }
5 ....
6 int a = 1, b = 2, c = 3;
7 int* max = Max(&a, &b, &c);
8 *max = 4; // c == 4;

下面我们思考一下C#中怎么合理的翻译这段代码。

/unsafe 指令

可能有的童鞋看到C/C++指针,已经想到了.NET编译指令中,开启/unsafe指令,它允许C#直接访问内存。的确,只要在项目中勾选“Allow unsafe code”。

就可以通过下面这种几乎和C/C++中一致方式来做到:

 1 unsafe static int* Max(int* first, int* second, int* third)
2 {
3 int* max = *first > *second ? first : second;
4 return *max > *third ? max : third;
5 }
6 ....
7 int a = 1, b = 2, c = 3;
8 unsafe
9 {
10 int* max = Max(&a, &b, &c);
11 *max = 4; // c == 4
12 }

但unsafe并不是C# 推荐使用的,它绕过了CLR的内存安全机制,指针的不安全滥用会被允许,容易使你的指针指到各种非预期的目标,比如允许访问已经返回(被释放)的调用栈(call stack),我们来做一个实验。

 1 unsafe static int* GetRef()
2 {
3 //Some codes
4 int i = 4;
5 return &i;
6 }
7 unsafe static void Main(string[] args)
8 {
9 int* num = GetRef();
10 Console.WriteLine(*num); // 4
11 //Some codes
12 Console.WriteLine(*num); // 不可预期
13 }

这是非常典型的一种错误,当GetRef()的调用返回后,它的调用堆栈被释放,我们尝试获取它本地的引用(num)时,如果GetRef遗留在内存的栈结构侥幸没有被重新分配,我们依然可以获取到。

但正常情况下,我们的逻辑一旦需要做一些其它处理(包括第一次Console.WriteLine()的调用本身),num所在的这块不安全内存自然会被覆盖。

虽然这是一段本身错误的代码,但站在语言层面,并没有做任何完全可以做的规避。(C/C++中同样存在这个问题)

返回模型对象

当然,其实C#6.0及以前,我们还有一种比较常见的方案:将有必要返回引用的值类型封装在一个寄宿模型类中。

由于对象以引用heap的地址传递,引用目标不在调用栈(call stack)上,不会由于函数返回而被释放。

1 static HostModel Max(HostModel first, HostModel second, HostModel third)
2 {
3 HostModel max = first.Value > second.Value ? first : second;
4 return max.Value > third.Value ? max : third;
5 }

这种类似做法被广泛应用在Model传递,DTO等场景中,无可厚非。。

但是如果在性能要求敏感,且数据和逻辑结构简单的场景下,为一个简单数据凭空多了一组装箱和拆箱动作,以对象形式在heap中申请本没有必要的内存,是一种非常浪费和奢侈的做法。

引用返回

C#7.0 中引入了引用返回(ref return)的概念,允许C#方法中返回一个值类型的引用。

Issue:#118。中给出了下面的例子:

 1 static ref int Max(ref int first, ref int second, ref int third)
2 {
3 ref int max = first > second ? ref first : ref second;
4 return max > third ? ref max : ref third;
5 }
6 …
7 int a = 1, b = 2, c = 3;
8 Max(ref a, ref b, ref c) = 4;
9 Debug.Assert(a == 1); // true
10 Debug.Assert(b == 2); // true
11 Debug.Assert(c == 4); // true

这样,我们通过C#7.0,能直接将调用栈(call stack)上的引用返回。

并且,对于体积较大的结构体(struct),返回引用比传递结构值要快很多,因为结构体的赋值会对整个结构进行拷贝。

另外需要注意的是,ref return的引用,在语言层面附加规则,不允许返回方法内的局部变量的引用,换句话说,被返回的堆栈地址,必须低于当前方法的入口地址。

总结

我们从另一个侧面看这个feature,其实是对性能要求极致情况下出现的考虑,对于目前大多数的.NET应用中,其实用例非常局限,也并非以往.NET侧重的方面。。

但是Roslyn项目在C#7.0设计初期就加入这个feature,是否隐含了更长远的考量?

我们再看看微软最近的新闻就不难理解了,本月初(6月1日)微软在北京举办的开发者峰会上,Satya Nadella宣布建立物联网实验室,峰会上还发布了微软的IoT套件。

近期微软还发布了Windows的IoT版本(Windows IoT),刚刚发布的.NET Core也允许跑在装有Windows IoT 的 Raspberry PI(树莓派)等设备上。

在这些对惜内存如金的端设备上,C#想要有一席用武之地,不可避免的需要一改以往对内存的任性的一些设计,也就可以理解了。这或许是C#7.0加入ref return的一个重要的原因。

本文链接:http://www.cnblogs.com/ylvict/p/5633480.html (转载请注明)

C# 7.0的更多相关文章

  1. ZAM 3D 制作简单的3D字幕 流程(二)

    原地址:http://www.cnblogs.com/yk250/p/5663907.html 文中表述仅为本人理解,若有偏差和错误请指正! 接着 ZAM 3D 制作简单的3D字幕 流程(一) .本篇 ...

  2. ZAM 3D 制作3D动画字幕 用于Xaml导出

    原地址-> http://www.cnblogs.com/yk250/p/5662788.html 介绍:对经常使用Blend做动画的人来说,ZAM 3D 也很好上手,专业制作3D素材的XAML ...

  3. 微信小程序省市区选择器对接数据库

    前言,小程序本身是带有地区选着器的(网站:https://mp.weixin.qq.com/debug/wxadoc/dev/component/picker.html),由于自己开发的程序的数据是很 ...

  4. osg编译日志

    1>------ 已启动全部重新生成: 项目: ZERO_CHECK, 配置: Debug x64 ------1> Checking Build System1> CMake do ...

  5. 【AR实验室】OpenGL ES绘制相机(OpenGL ES 1.0版本)

    0x00 - 前言 之前做一些移动端的AR应用以及目前看到的一些AR应用,基本上都是这样一个套路:手机背景显示现实场景,然后在该背景上进行图形学绘制.至于图形学绘制时,相机外参的解算使用的是V-SLA ...

  6. Elasticsearch 5.0 中term 查询和match 查询的认识

    Elasticsearch 5.0 关于term query和match query的认识 一.基本情况 前言:term query和match query牵扯的东西比较多,例如分词器.mapping ...

  7. Swift3.0服务端开发(一) 完整示例概述及Perfect环境搭建与配置(服务端+iOS端)

    本篇博客算是一个开头,接下来会持续更新使用Swift3.0开发服务端相关的博客.当然,我们使用目前使用Swift开发服务端较为成熟的框架Perfect来实现.Perfect框架是加拿大一个创业团队开发 ...

  8. vue2.0实践的一些细节

    最近用vue2.0做了个活动.做完了回头发现,好像并没有太多的技术难点,而自己好像又做了比较久...只能说效率有待提升啊...简单总结了一些比较细节的点. 1.对于一些已知肯定会有数据的模块,先用一个 ...

  9. Linux平台 Oracle 10gR2(10.2.0.5)RAC安装 Part3:db安装和升级

    Linux平台 Oracle 10gR2(10.2.0.5)RAC安装 Part3:db安装和升级 环境:OEL 5.7 + Oracle 10.2.0.5 RAC 5.安装Database软件 5. ...

  10. Linux平台 Oracle 10gR2(10.2.0.5)RAC安装 Part1:准备工作

    Linux平台 Oracle 10gR2(10.2.0.5)RAC安装 Part1:准备工作 环境:OEL 5.7 + Oracle 10.2.0.5 RAC 1.实施前准备工作 1.1 服务器安装操 ...

随机推荐

  1. Asp.net Role manager tutorial

    It is very useful in .net we can user framework provided role manager, and easily configure in Web.C ...

  2. oracle命令大全

    内容包括三大项:    1.oracle基本操作语句 2.SQLServer基本操作语句 3.各种数据库连接方法 ******************************************* ...

  3. UISegmentedControl(分页控制器) AND UISlider(滑块控制器)

    - (void)viewDidLoad { [super viewDidLoad]; UIImageView * animatedImageView = [[UIImageView alloc]ini ...

  4. HDU 1711 Number Sequence KMP

    题目地址: http://acm.hdu.edu.cn/showproblem.php?pid=1711 AC代码: #include <iostream> #include <cs ...

  5. c#与.NET的区别

    C#与.NET的关系 C# 可以通过.NET平台来编写 部署 运行.NET应用程序VB.NET.......NET语言 C#是专门为.NET平台而生的(面向对象) .NET平台的重要组成:1.FCL- ...

  6. C函数调用与栈--代码真相

    前面详细的说了,C函数调用的过程中,栈的变化情况的原理部分,这里在看一下汇编代码的真正的实现. 有关前面的那一片博客,主要记住的就是函数调用时栈的变化,4+3+2的步骤: (1)设置栈帧边界 (2)开 ...

  7. Java 操作mongodb

    package cn.test.db; import java.io.File; import java.io.IOException; import java.net.UnknownHostExce ...

  8. 运行PHP

    /usr/local/php/bin/php /home/www/index.php

  9. 一些安全相关的HTTP响应头

    转:http://www.2cto.com/Article/201307/230740.html 现代浏览器提供了一些安全相关的响应头,使用这些响应头一般只需要修改服务器配置即可,不需要修改程序代码, ...

  10. bootstrap基础知识

    Bootstrap是Twitter推出的一款简洁.直观.强悍的前端开发框架. Bootstrap基于 HTML.CSS.JAVASCRIPT.它由Twitter的设计师Mark Otto和Jacob ...