【C#進階系列】08 關於參數的故事

来源:http://www.cnblogs.com/vvjiang/archive/2016/03/11/5264123.html
-Advertisement-
Play Games

可選參數和命名參數 不多說,上代碼,自然懂 class Program { static void Main(string[] args) { var troy = new Troy(); troy.HelloWorld(1);//此時b和c都為0 troy.HelloWorld(1,2);//此時


可選參數和命名參數

不多說,上代碼,自然懂

  class Program
    {
        static void Main(string[] args)
        {
            var troy = new Troy();
            troy.HelloWorld(1);//此時b和c都為0
            troy.HelloWorld(1,2);//此時b為2,c為0,以上兩個為可選參數的玩法

            troy.HelloWorld(a: 1, b: 2);//命名參數玩法
            troy.HelloWorld(b: 2, a: 1);//即使順序打亂,效果也是一樣
        }
    }
    public class Troy {
        public void HelloWorld(int a, int b = 0,int c=default(int)) {//這裡b和c參數就是可選參數
            //註意default(int)這種玩法,表示int的預設值。
            //我是第一次知道這種用法,然而非常推崇這樣的玩法,因為可以有效減少你代碼中的魔法數字。也許你認為0這種不算魔法數字,然而我認為能讓代碼更簡單易懂一點點也是非常有必要的。
            //就算不用魔法數字,那麼default(DateTime)去判斷DateTime值是否為預設值,是不是比new Datetime()更好一點呢?
        }
    }

預設參數實際上在C#編譯器編譯過後就向該參數應用特性OptionalAttribute和DefaultParameterValueAttribute。並不是CLR支持的,而是C#特有的。

這兩個東西看上去都那麼美好,然而美好的東西並不一定真的好。

推薦用命令參數,然而不要為了調換順序而調換順序。實際上對VS這麼強大的工具而言,命名參數只有在參數非常多的時候才有用。

如果你的參數太多,那麼其實更應該考慮縮小一下參數的數量。可以考慮提取一個參數對象,傳值的時候傳這個參數對象就好了。

雖然我非常喜歡用預設參數,實際上它也確實很好用,特別對於重載而言,你有的時候根本沒必要去寫兩個函數。

然而這實際上也是個坑點。

如果你和我一樣喜歡用,你團隊的人也喜歡用。那麼最後你會發現,這個東西總是會讓函數內部充滿了各種各樣的分支,你的參數列表也會越來越長。o(︶︿︶)o 唉

如果你不懂那麼可以看一下我寫的一個代碼維護小故事,請想象一下你在維護的是一個業務複雜的大型系統,那麼接下來的節奏會經常發生。

     //最開始Troy寫了一個打招呼的函數
        public void 打招呼()
        {
            Console.WriteLine("你好");
            //下麵還有一系列握手,微笑之類的操作
        }
        //後來老闆說國際化,也要能英文打招呼.你選擇了預設參數,並且為了保證以前的代碼順利運行,預設為false
        public void 打招呼(bool IsEnglish=false)
        {
            if (IsEnglish)
            {
                Console.WriteLine("Hello");
            }
            else {
                Console.WriteLine("你好");
            }
            //下麵還有一系列握手,微笑之類的操作
        }
        //到了上面那一步也是OK的,然而過了兩天,老闆跟大牛說有的老外有可能不握手,他選擇擁抱。於是大牛改代碼:
        public void 打招呼(bool IsEnglish = false, bool 是否選擇擁抱 = false)
        {
            if (IsEnglish)
            {
                Console.WriteLine("Hello");
            }
            else {
                Console.WriteLine("你好");
            }
            if (是否選擇擁抱)
            {
                Console.WriteLine("擁抱");
            }
            else {
                Console.WriteLine("握手");
            }
            //下麵還有一系列微笑之類的操作
        }

當然即使到現在,以上的代碼看起來也僅僅只是分支增多而已,然而請你設身處地去想一下,如果這個系統的業務很複雜,如果後面還有需求,如果不僅僅只是一個Console.WriteLine這麼簡單的操作。

等到第四次去修改的時候,新來的項目組成員小菜已經沒得選了。首先他不熟悉業務,不敢亂改,他甚至可能熟悉也懶得改,因為改起來已經很麻煩了(不要太相信你的隊友,我就是這樣的懶人(☆_☆)),那麼這個時候他只能默默選擇再加一個預設參數。

有第四個,肯定就會有第五個,每次想要重構感覺難度越來越大,心裡越來越虛,只好隨大流去加預設參數,經過大家一起努力,這段代碼已經差不多10個分支了,大家都不敢改了。

如果從一開始不選擇加預設參數,而是多寫一個重載函數,將公共部分提煉出來,那麼你覺得還會有這樣的問題嗎?

然而並沒有什麼鳥用,即使是我瞭解的這麼清楚,我經常會覺得我在加第三次預設參數完全沒有任何問題,偷點懶趕緊下班啦,代碼依然能看,等我下次回來改的時候,我發現預設參數已經變成5個了。o(︶︿︶)o 唉

out和ref的故事

CLR不區分out和ref,生成的IL代碼一模一樣,只是元數據會有個bit值加以區分。

out表示傳遞的是引用類型,然而他並不需要調用的時候這個參數就初始化完畢,且要求函數執行完畢的時候out參數必須被寫入過。

ref要求調用的時候這個參數就初始化完畢,且不要求函數執行完畢的時候ref參數必須被寫入過。

實際上在C裡面這個東西就是指針,我們這裡來講這東西傳遞的就是值的地址。

如果有大的值類型的傳參,比如一個大型struct。

那麼用這種方法傳參只會傳一個地址值,而不是把每個struct裡面的值都壓到棧中。

另外不要因為ref比out好用就不用out,out可以明確你這個參數一定會傳值出來,讀代碼更容易。

pramas傳遞可變數量的參數

這個網上一大堆,我用得最多的就是String.Format方法,可以參考這個來瞭解。

不過要註意這個會有性能損失,因為傳遞的pramas一維數組,實際上是分配在堆空間中的,初始化啊,垃圾回收啊,都會有影響。

參數和返回類型的設計規範

聲明方法的參數類型時,應該按照最低介面類型,更強的介面類型,基類,派生類從高到低的優先順序來聲明類型。

因為用介面或者基類會讓你的方法更加靈活,可以選擇的餘地更大。

而返回類型正好相反,防止受限於特定類型。

以上是作者講的,有道理,但是具體情況具體分析,難道要我們都去聲明object,那一切都OK了?

作者的意思顯然不是如此。我認為你可以在第一次的時候去選擇最適合的強類型參數,但是下一次有一個類似的函數時,而兩個參數間有一個共同的介面或者基類,那麼是否可以考慮一下將參數的類型弱化呢?這樣明顯可以讓代碼更靈活。

但是不要因此直接就用個object之類的,不要過度設計,永遠用目前最滿足需求的那個,不要把未來全部考慮完全,因為你根本預測不到未來。

 


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

-Advertisement-
Play Games
更多相關文章
  • 項目為愛鮮蜂,一款電商APP,使用語言Swift2.0,開發工具Xcode7.0.1. 項目為純代碼開發,沒有使用XIB和StoryBoard.開發周期大概為2個月左右(工作閑暇之餘). 數據都是本地數據,輔助開發軟體:PhotoShop CS6(圖片處理),Charles(抓包工具). 寫的比較匆
  • oracle基礎
  • 有些時候,有些作業遇到問題執行時間過長,因此我寫了一個腳本可以根據歷史記錄,找出執行時間過長的作業,在監控中就可以及時發現這些作業並儘早解決,代碼如下: SELECT sj.name , sja.start_execution_date,DATEDIFF (SECOND ,sja.start_exe...
  • 五、bash運算及啟動腳本01.使用bash的命令歷史#history……#set(顯示所有的變數) | grep HISHISTFILE=/root/.bash_historyHISTFILESIZE=1000(歷史文件個數)HISTSIZE=1000(文件的歷史大小)#vi /root/.bas
  • 序列化是將一個對象轉換成位元組流以達到將其長期保存在記憶體、資料庫或文件中的處理過程。它的主要目的是保存對象的狀態以便以後需要的時候使用。與其相反的過程叫做反序列化。 序列化一個對象 為了序列化一個對象,我們需要一個被序列化的對象,一個容納被序列化了的對象的(位元組)流和一個格式化器。進行序列化之前我們先
  • 註冊用戶有一段時間了,一直很忙,到現在還沒有寫一篇,忽然覺的一定要花點時間記錄和總結一些東西。好吧,就從這裡開始了。 今天客戶提出要點擊菜單(TreeView實現的)的父級節點時,展開節點。心想這個應該是很常見的功能吧,特意google了一下,發現大部分是將的不是js實現的,有些js實現的寫的麻煩,
  • 它們只是不起眼的小技巧。日積月累,它們讓我們的工作、學習更有效率,讓我們更加專註於邏輯本身,它們是.NET程式員的好朋友,它們是Visual Studio的小技巧……我們,真的認識它們嗎? 如果想儘快掌握這些技巧,請打開Visual Studio親自試一下這些技巧,希望找到你喜歡的技巧的。 (圖片來
  • 泛型(generic)是C#語言2.0和通用語言運行時(CLR)的一個新特性。泛型為.NET框架引入了類型參數(type parameters)的概念。類型參數使得設計類和方法時,不必確定一個或多個具體參數,其的具體參數可延遲到客戶代碼中聲明、實現。這意味著使用泛型的類型參數T,寫一個類MyList
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...