有關.NET參數傳遞方式的思考

来源:http://www.cnblogs.com/pengze0902/archive/2016/12/27/6221837.html
-Advertisement-
Play Games

年關將近,整個人已經沒有了工作和寫作的激情,估計這個時候很多人跟我差不多,該相親的相親,該聚會喝酒的聚會喝酒,總之就是沒有了幹活的心思(我有很多想法,但就是叫不動我的手腳,所以我只能看著別人在做我想做的事,吃我想吃的東西。)。本人由上個月的每周四五篇,到現在的文章縮短到每周一篇,說個實話,現在的一篇 ...


   年關將近,整個人已經沒有了工作和寫作的激情,估計這個時候很多人跟我差不多,該相親的相親,該聚會喝酒的聚會喝酒,總之就是沒有了幹活的心思(我有很多想法,但就是叫不動我的手腳,所以我只能看著別人在做我想做的事,吃我想吃的東西。)。本人由上個月的每周四五篇,到現在的文章縮短到每周一篇,說個實話,現在的一篇也有不想寫的心思了(這一篇還是咬著牙寫的,感覺實在是寫不動了,寫博客太折騰人了,誰寫誰知道啊!),不過還是希望寫出來可以幫到大家,如有寫的不足的地方,還望大家多多指正,知識在於總結和反思,對別人也對自己都是一個提高。  

   這裡先來一段廢話,緩和一下氣氛,免得讓大家很尷尬(太直接了還是不太好,總不能見到喜歡的女生就表白吧,還得多多的相處,讓人覺得你穩重有深度。),現在進入我們今天的博客內容,那就是.NET的參數用法。因為在.NET的參數用法和約束特別多,對於很多初學者來說,這樣繁多的參數用戶簡直就是跟扯淡一樣,即使對於是擁有豐富經驗的開發者來說,也未必能夠很輕鬆使用所有的參數用法和選擇合適的參數類型。談到參數,估計很多人就只是想著我們在一般的方法調用中使用的那樣,如string,int,object等等類型,更多的也就沒有了印象,就是知道,也就是在遇到了再去查看一下,這樣其實也沒錯,畢竟不能話費過多的時間用在哪些不常用的知識上,但是我個人覺得對於知識還是需要提前有一個全面的學習,可能具體的細節不能很好的把握,但是對於全局的概念還是得有一個整體的學習。

   下麵就簡單的介紹一下.NET的一些常用參數用法,如有不足還望指正,也歡迎大家在下麵留言討論,分享自己的見解。

一.DotNet參數概述:

    .NET中參數(形式參數)變數是方法或索引器聲明的一部分,而實參是調用方法或索引器時使用的表達式。

    在CLR中,預設的情況下所有的方法參數都是傳值的。在傳遞引用類型的對象時,對一個對象的引用會傳遞給方法。這裡的船引用本身是以傳值的方式傳給方法的。這也意味著方法能夠修改對象,而調用者能看到這些修改。對於值類型的實例,傳給方法的實例的一個副本。意味著方法將獲得它專用的一個值類型實例副本,調用者中的實例不受影響。

    在CLR中允許以傳引用而非傳值的方式傳遞參數,在C#中使用out和ref來實現傳遞引用的方式傳值。在C#中使用out和ref來實現傳遞引用的方式傳值,這兩個關鍵字告訴編譯器生成元數據來指明該參數是傳引用的,編譯器將生成代碼來傳遞參數的地址,而不是傳遞參數本身。為值類型使用out和ref,效果等同於以傳值的方式傳遞引用類型。   

    常用的參數主要有基本類型參數,泛型參數,以及<in T>和<out T>,dynamic等等。例如<in T>和<out T>,在CLR中支持泛型類型的可變性,C#在4.0時獲得了生命泛型遍體所必須的語法,並且現在編譯器也能夠知道介面和委托可能的轉換。可變性是以一種類型安全的方式,講一個對象作為另一個對象來使用。可變性應用於泛型介面和泛型委托的類型參數中。協變形用於向調用者返回某項操作的值;逆變性是指調用者想API傳入值;不變性是相對於協變性和逆變性,是指什麼也不會發生。對於這方面的知識非常的豐富,有興趣的可以自行瞭解,這裡就不做詳細的介紹了。dynamic類型,C#是一門靜態類型的語言,在某些情況下,C#編譯器要尋找特定的名稱而不是介面。dynamic可以在編譯時做任何事,到執行時再由框架進行處理。有關動態類型的介紹也不做更深入的介紹。

    在.NET中參數的使用方法主要為可選參數、命名參數、可變數量參數等等。本文下麵也是主要介紹這三種參數的使用方法。

二.DotNet參數用法:

    以下是主要介紹三種參數的用法:可選參數;命名實參;傳遞可變數量的參數。   

   1.可選參數:

     (1).基本用法:

        如果某個操作需要多個值,而有些值在每次調用的時候又往往是相同的,這時通常可以使用可選參數。在C#以前實現可變參數的功能,往往聲明一個包含所有可能參數的方法,其他方法調用這個方法,並傳遞恰當的預設值。

        在可選參數中,設計一個方法的參數時,可以為部分或全部參數分配預設值。在調用這些方法代碼可以選擇不指定部分實參,接受預設值。還可以在調用方法時,還可以通過指定參數名稱的方式為其傳遞實參。如下實例:

        static void OptionalParameters(int x, int y = 10, int z = 20)
        {
            Console.WriteLine("x={0} y={1} z={2}",x,y,z);
        }

         OptionalParameters(1, 2, 3);
         OptionalParameters(1, 2);
         OptionalParameters(1);

     以上的例子可以很清楚的看到其用法,int y=10和int z=20這兩個參數就是可選參數。可選參數的使用中,如果調用時省略了一個參數,C#編譯器會自動嵌入參數的預設值。向方法傳遞實參時,編譯器按從左向右的順序對實參進行求值。使用已命名的參數傳遞實參時,編譯器仍然按照從左到右的順序對實參進行求值。

      (2).基本原則:

       可選參數包含一些規範,具體的一些要求如下:

    (a).所有可選參數必須出現在必備參數之後,參數數組(使用params修飾符聲明)除外,但他們必須出現在參數列表的最後,在他們之前是可選參數。

    (b).參數數組不能聲明為可選的,如果調用者沒有指定值,將使用空數組代替。

    (c).可選參數不能使用ref和out修飾符。

    (d).可選參數可以為任何類型,但對於指定的預設值卻有一些限制,那就是預設值必須為常量(數字或字元串字面量、null、const成員、枚舉成員、default(T)操作符)。

    (e).指定的值會隱式轉換為參數類型,但是這種轉換不能是用戶定義的。

    (f).可以為方法、構造器、有參屬性的參數指定預設值,還可以為屬於委托定一部分的參數指定預設值。

    (g).C#不允許省略逗號之間的實參。

      在使用可選參數時,對於引用類型使用null來做預設值,如果參數類型是值類型,只需要使用相應的可空值類型作為預設值。

      (3).代碼示例:

        /// <summary>
        /// 提取異常及其內部異常堆棧跟蹤
        /// </summary>
        /// <param name="exception">提取的例外</param>
        /// <param name="lastStackTrace">最後提取的堆棧跟蹤(對於遞歸), String.Empty or null</param>
        /// <param name="exCount">提取的堆棧數(對於遞歸)</param>
        /// <returns>Syste.String</returns>
        public static string ExtractAllStackTrace(this Exception exception, string lastStackTrace = null, int exCount = 1)
        {
            while (true)
            {
                var ex = exception;
                const string entryFormat = "#{0}: {1}\r\n{2}";
                lastStackTrace = lastStackTrace ?? string.Empty;
                lastStackTrace += string.Format(entryFormat, exCount, ex.Message, ex.StackTrace);
                if (exception.Data.Count > 0)
                {
                    lastStackTrace += "\r\n    Data: ";
                    lastStackTrace = exception.Data.Cast<DictionaryEntry>().Aggregate(lastStackTrace, (current, entry) => current + $"\r\n\t{entry.Key}: {exception.Data[entry.Key]}");
                }
                //遞歸添加內部異常
                if ((ex = ex.InnerException) == null) return lastStackTrace;
                exception = ex;
                lastStackTrace = $"{lastStackTrace}\r\n\r\n";
                exCount = ++exCount;
            }
        }

   2.命名實參:

         以上講解了可選參數的一些基本概念和用法,接下來看一下命名參數的相關操作用法:

      (1).基本用法:

          命名實參是指在指定實參的值時,可以同時指定相應的參數名稱。編譯器將判斷參數的名稱是否正確,並將指定的值賦給這個參數。命名參數在各個實參之前加上它們的參數名稱以及一個冒號。如下代碼:

new StreamWriter(path:filename,aooend:true,encoding:realEncoding);

 如果要對包含ref和out的參數指定名稱,需要將ref和out修飾符放在名稱之後,實參之前。

int number;
bool success=int.TryParse("10",result:out number);

      (2).基本原則:

        在命名參數中,所有的命名參數必須位於位置實參之後,兩者之間的位置不能改變。位置實參總是指向方法聲明中相應的參數,不能跳過參數之後,在通過命名相應位置的實參來指定。實參仍然按編寫順序求值,即使這個順序有可能會不同於參數的聲明順序。

        在一般情況下,可選參數與命名實參會一起配合使用。可選參數會增加適用方法的數量,而命名實參會減少使用方法的數量。為了檢查是否存在特定的適用方法,編譯器會使用位置參數的順序構建一個傳入實參的列表,然後對命名實參和剩餘的參數進行匹配。如果沒有指定某個必備參數,或某個命名實參不能與剩餘的參數相匹配,那麼這個方法就不是適用的。

       命名實參有時可以代替強制轉換,來輔助編譯器進行重載決策。如果方法是從模塊的外部調用的,更改參數的預設值是具有潛在的危險的。可以按名稱將實參傳給沒有預設值的參數,但是編譯器要想編譯代碼,所有要求的實參都必須傳遞。

       在寫C#代碼與COM對象模型進行互操作時,C#的可選參數和命名參數功能是最好用的,調用一個COM組件時,為了以傳引用的方式傳遞一個實參,C#還允許省略REF/OUT,在嗲用COM組件時,C#要求必須向實參應用OUT.REF關鍵字。    

   3.傳遞可變數量的參數:

      在項目開發中,有時我們需要定義一個方法來獲取可變數量的參數。可以使用params,params只能應用於方法簽名中的最後一個參數。params關鍵字告訴編譯器向參數應用System.ParamArrayAttribute的實例。我們具體看一下實現的代碼:

[AttributeUsage(AttributeTargets.Parameter, Inherited=true, AllowMultiple=false), ComVisible(true), __DynamicallyInvokable]
public sealed class ParamArrayAttribute : Attribute
{
    // Methods
    [__DynamicallyInvokable]
    public ParamArrayAttribute();
}

 
[__DynamicallyInvokable]
public ParamArrayAttribute()
{
}

     以上的代碼可以看出該類繼承自Attribute類,對於Attribute類可能不會陌生,那就是定義定製屬性的基類,說明ParamArrayAttribute類用於定義定製屬性,ParamArrayAttribute類在System命名空間下,ParamArrayAttribute類只有一個構造方法,沒有具體的實現。AttributeUsage也定義了屬性的使用方式。

    C#編譯器檢測到一個方法調用時,會檢查所有具有指定名稱、同時參數沒有應用ParamArrayAttribute的方法。如果找到一個匹配的方法,編譯器生成調用它所需的代碼。如果編譯器沒有找到一個匹配的方法,會直接檢查應用ParamArrayAttribute的方法。如果找到一個匹配的方法,編譯器會先生成代碼來構造一個數組,填充它的元素,再生成代碼來調用選定的方法。

    調用一個參數數量可變的方法時,會造成一些額外的性能損失,數組對象必須在對上分配,數組元素必須初始化,而且數組的記憶體最終必須垃圾回收。

    提供一個方法代碼,僅供參考:

        /// <summary>
        /// 字元型二維數組轉換成DataTable 
        /// </summary>
        /// <param name="stringDyadicArray"></param>
        /// <param name="messageOut"></param>
        /// <param name="dataTableColumnsName"></param>
        /// <returns></returns>
        public DataTable DyadicArrayToDataTable(string[,] stringDyadicArray, out bool messageOut,
            params object[] dataTableColumnsName)
        {
            if (stringDyadicArray == null)
            {
                throw new ArgumentNullException("stringDyadicArray");
            }
            var returnDataTable = new DataTable();
            if (dataTableColumnsName.Length != stringDyadicArray.GetLength(1))
            {
                messageOut = false;
                return returnDataTable;
            }
            for (var dataTableColumnsCount = 0;dataTableColumnsCount < dataTableColumnsName.Length;dataTableColumnsCount++)
            {
                returnDataTable.Columns.Add(dataTableColumnsName[dataTableColumnsCount].ToString());
            }
            for (var dyadicArrayRow = 0; dyadicArrayRow < stringDyadicArray.GetLength(0); dyadicArrayRow++)
            {
                var addDataRow = returnDataTable.NewRow();
                for (var dyadicArrayColumns = 0; dyadicArrayColumns < stringDyadicArray.GetLength(1);dyadicArrayColumns++)
                {
                    addDataRow[dataTableColumnsName[dyadicArrayColumns].ToString()] = stringDyadicArray[dyadicArrayRow, dyadicArrayColumns];
                }
                returnDataTable.Rows.Add(addDataRow);
            }
            messageOut = true;
            return returnDataTable;
        }

   以上給出了一個使用可變參數數量以及命名參數的使用樣例,完成了將二維位元組數組轉化為DataTable對象,將數組進行遍歷,並將數組寫入datatable中,對於整個方法的邏輯就不做深入介紹,代碼比較的簡單。

三.與參數有關的一些指導原則:

    聲明方法的參數類型時,應儘量指定最弱的類型,最好是介面而不是基類。

    在設計模式的基本原則中,迪米特法則也較最少知識原則,迪米特法則是指如果兩個類不必彼此直接通信,那麼這兩個類就不應當直接的相互作用。如果其中一個類需要調用另一個類的某一個方法的話,可以通過第三者轉發這個調用。在類結構的設計上,每一個類都應當儘量降低成員的訪問許可權。類之間的耦合度越弱,越有利於復用,一個處在弱耦合的類被修改,不會對有關係的類造成波及。

    對於參數的使用中,我們在對參數類型的使用上,還是需要很仔細和認真的去思考,因為在參數類型的定義上,在一定程度上影響著我們程式的擴展性和穩定性,如果參數類型的約束比較大,對於後續方法的擴展,意義是巨大的。在整個面向對象的語言體系中,一切設計模式都是由“多態”延伸而來,對於介面和委托都是在我們面向對象設計中使用很多的,目的較多的是在使用時擴大參數的約束性。

    在方法的返回值類型中,返回的類型應該聲明為最強的類型,以免受限於特定的類型。

四.總結:

   以上是一篇簡單介紹方法參數的文章,在文章內容中主要對於介紹可選參數、命名參數等。以上的內容如果有不足的地方還望大家多多包涵,也希望能夠指出對應的問題。知識先於模範,後於反思。學習完一點後,需要我們去總結和反思,其中的內涵我們才會有時間和精力,以及由能力去思考。


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

-Advertisement-
Play Games
更多相關文章
  • 首先要有一個樹莓派3B , 在某寶買就行, 這東西基本上找到假貨都難,另外國產和英國也沒什麼差別,差不多哪個便宜買哪個就行。 不要買店家的套餐,一個是配的東西有些不需要,有的質量也不好。 提示:除了GPIO以外都不要熱插拔,所有東西都在安裝好後最後啟動電源。 1.下載系統 截止目前 最新的是2016 ...
  • 今天突然練習linux命令行的時候,想在window中聯繫linux命令行。經過一番dudu找到了一個不錯的的工具(SecureCRT--意思安全)就是用SSH鏈接linux主機。推薦大家使用。畢竟window下放個小曲,下點小電影同時練習linux命令行哈!~感謝強大的VMware11 找了不少博 ...
  • Linux硬體IO的優化簡介 首先簡單介紹下有哪些硬體設備如下(由於硬體種類廠家等各種因素我就不在此多做介紹有興趣的可以自行學習): 1.CPU:中央處理器,是電腦運算控制的核心部件之一,相當於人的大腦。如下圖 2.RAM:記憶體條與CPU溝通的橋梁,很多數據和運算在記憶體中做臨時處理,相當於你在做快 ...
  • 分享一個MySQL分庫備份腳本(原) 開發思路: 1.路徑:規定備份到什麼位置,把路徑(先判斷是否存在,不存在創建一個目錄)先定義好,我的路徑:/mysql/backup,每個備份用壓縮提升效率,帶上時間方便整理 2.取資料庫:抓取資料庫名稱,我用的awk和grep配合取資料庫的名稱(如果想按照表備 ...
  • 自己簡單寫的一個php服務的啟動腳本和大家分享 思路(實現的原理): 1:function模塊+case語句多分支判斷 2:通過添加# chkconfig: 2345 43 89註釋實現開機自啟動(前提是把腳本放入/etc/init.d/目錄下 然後chmod給可執行許可權,然後chkconfig - ...
  • MySQL(或者其它服務)的keepalived高可用監控腳本 開發腳本需求:我們知道,keepalive是基於虛擬ip的存活來判斷是否搶占master的機制的,但是如果我們做了MySQL的keepalived的高可用的時候,就要考慮一種情況的發生,那就是如果機器網卡並沒有斷,二十由於MySQL服務 ...
  • 驗證碼類 使用方法 HTML 視圖JS ...
  • 1 自定義控制項與用戶控制項區別 WinForm中, 用戶控制項(User Control):繼承自 UserControl,主要用於開發 Container 控制項,Container控制項可以添加其他Controls控制項 自定義控制項(Custom Control):繼承自 Control,主要用於開發wi ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...