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

来源:http://www.cnblogs.com/ylvict/archive/2016/07/01/5633480.html
-Advertisement-
Play Games

本文參考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月),就 ...


本文參考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 (轉載請註明)

目前(2016年7月)C#7.0還未正式發佈,大家如果想體驗部分特性,可以去下載VS15預覽版,最終發佈的語法可能和本文中提及的有所不同,最新動態請大家關註Roslyn項目。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、使用yum安裝 yum -y install httpd mysql mysql-server php php-mysql postgresql postgresql-server php-postgresql php-pgsql php-devel 2、配置httpd 2.1、啟動httpd服 ...
  • 互斥量和臨界區非常相似,只有擁有了互斥對象的線程才可以訪問共用資源,而互斥對象只有一個,因此可以保證同一時刻有且僅有一個線程可以訪問共用資源,達到線程同步的目的。 互斥量相對於臨界區更為高級,可以對互斥量進行命名,支持跨進程的線程同步。互斥量是調用的Win32的API對互斥鎖的操作,因此在同一操作系 ...
  • ...
  • 來源:威客百科 本文地址:baike.renwuyi.com/2015-04/9181.html 轉載請註明出處。 ...
  • 1、進入支付寶首頁 GET https://auth.alipay.com/login/index.htm HTTP/1.1Accept: */*Accept-Language: zh-CNAccept-Encoding: gzip, deflateUser-Agent: Mozilla/4.0 ( ...
  • 很早之前看到過RESTful Web Services,並未在意,也沒找相關資料進行學習。今天偶爾有一機會,就找了點資料進行研究,發現RESTful真是“簡約而不簡單”。下麵用示例來說明: 1 項目結構 2 REST 服務介面定義 3 REST服務介面實現 4 將服務HOST 5 打開瀏覽器,即可進 ...
  • 一. 結構體的傳遞 Cpp代碼 #define JNAAPI extern "C" __declspec(dllexport) // C方式導出函數 typedef struct { int osVersion; int majorVersion; int minorVersion; int bui ...
  • 近段時間,有實現一個看板的功能,就是用戶更新信息時,即是對資料庫的數據進行插入,更新,或是刪除時,在牆上的屏幕的數據不需要人為去刷新,用戶就能看到更新後的數據。實現此功能,Insus.NET使用SignalR的技術來實現。下麵的實例中,雖然不是實際的數據,但是實現過程是一樣的,現在分享給大家學習學習 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...