C 7 0 新特性4 返回引用

2022-01-29 19:17:57 字數 3191 閱讀 5749

本文參考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中(2023年1月),就借鑑並延續了c/c++中指標引數,原生允許將值型別資料的引用(指標)通過標記ref引數的形式,傳遞到方法體中。

但對於方法內的值型別引用,該如何以引用的方式返回,卻一直以來沒有乙個非常完美的解決方案,儘管這種用例非常少見。

提乙個簡單的問題,我們需要獲取三個int中的最大值的引用

我們照慣例,回顧下c#7.0之前的做法:

我們回歸到c/c++中,這個問題沒有什麼好爭議的,實現起來會很理所應當的是這樣的:

1

int* max(int* first, int* second, int*third)

5....

6int a = 1, b = 2, c = 3;7

int* max = max(&a, &b, &c);

8 *max = 4; //

c == 4;

下面我們思考一下c#中怎麼合理的翻譯這段**。

可能有的童鞋看到c/c++指標,已經想到了.net編譯指令中,開啟/unsafe指令,它允許c#直接訪問記憶體。的確,只要在專案中勾選「allow unsafe code」。

就可以通過下面這種幾乎和c/c++中一致方式來做到:

1

unsafe

static

int* max(int* first, int* second, int*third)26

....

7int a = 1, b = 2, c = 3;8

unsafe

9

但unsafe並不是c# 推薦使用的,它繞過了clr的記憶體安全機制,指標的不安全濫用會被允許,容易使你的指標指到各種非預期的目標,比如允許訪問已經返回(被釋放)的呼叫棧(call stack),我們來做乙個實驗。

1

unsafe

static

int*getref()27

unsafe

static

void main(string

args)

8

這是非常典型的一種錯誤,當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

這種類似做法被廣泛應用在model傳遞,dto等場景中,無可厚非。。

但是如果在效能要求敏感,且資料和邏輯結構簡單的場景下,為乙個簡單資料憑空多了一組裝箱和拆箱動作,以物件形式在heap中申請本沒有必要的記憶體,是一種非常浪費和奢侈的做法。

c#7.0 中引入了引用返回(ref return)的概念,允許c#方法中返回乙個值型別的引用。

issue:#118。中給出了下面的例子:

1

static

refint max(ref

int first, ref

int second, ref

intthird)26

…7int 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的乙個重要的原因。

C 7 0 特性例項

using system using system.collections.generic using system.linq using system.text using system.threading.tasks namespace csharp 7.0 a,b function ref r...

C 7 0新特性和語法糖詳解

伴隨visual studio 2017的發布,c 7.0開始正式走上工作崗位。對於早已熟悉了舊版本c 的開發者來說,c 7.0增加的不少新特性和語法糖能在很大程度上提公升程式設計效率並降低出錯率。本文將闡述c 7.0給出的9個改進。1 元組 更優雅地返回多個值 之所以將元組放在第一位,是因為它對c...

C 函式新特性 引用型別

引用是已定義的變數的別名,主要用途是用作函式的形參。通過將引用變數作為形參可以直接使用原始資料而不是新建乙個副本。必須在宣告引用變數的時候進行初始化 int rat int rodent rodent rat 不可以這樣做!而應該這樣 int rat int rodent rat 正確!比較下面交換...