本文參考Roslyn項目中的Issue:#118。html
1. C# 7.0 新特性1: 基於Tuple的「多」返回值方法git
2. C# 7.0 新特性2: 本地方法github
3. C# 7.0 新特性3: 模式匹配windows
4. C# 7.0 新特性4: 返回引用安全
C#早在最初的發行版C# 1.0中(2002年1月),就借鑑並延續了C/C++中指針參數,原生容許將值類型數據的引用(指針)經過標記ref參數的形式,傳遞到方法體中。函數
但對於方法內的值類型引用,該如何以引用的方式返回,卻一直以來沒有一個很是完美的解決方案,儘管這種用例很是少見。visual-studio
提一個簡單的問題,咱們須要獲取三個int中的最大值的引用。性能
咱們照慣例,回顧下C#7.0以前的作法:spa
咱們迴歸到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#中怎麼合理的翻譯這段代碼。
可能有的童鞋看到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 (轉載請註明)
目前(2016年7月)C#7.0還未正式發佈,你們若是想體驗部分特性,能夠去下載VS15預覽版,最終發佈的語法可能和本文中說起的有所不一樣,最新動態請你們關注Roslyn項目。