<NET CLR via c# 第4版> 第9章 參數

来源:http://www.cnblogs.com/harry-wang/archive/2017/06/02/6868365.html
-Advertisement-
Play Games

9.1 可選參數和命名參數 9.1.1 規則和原則 可為方法、構造器方法和有參屬性(C 索引器)的參數指定預設值。還可為屬於委托定義一部分的參數指定預設值。 有預設值的參數必須放在沒有預設值的所有參數之後。但有一個例外:“參數數組”這種參數必須放在所有參數(包括有預設值的這些)之後,而且數組本身不能 ...


9.1 可選參數和命名參數

    class Program
    {
        private static int s_n = 0;

        private static void M(int x = 9, string s = "A", DateTime dt = default(DateTime), Guid guid = new Guid())
        {
            Console.WriteLine("x={0},s={1},dt={2},guid={3}", x, s, dt, guid);
        }
        public static void Main()
        {
            //1.等同於M(9,"A",default(DateTime),new Guid());
            M();

            //2.等同於M(8,"X",default(DateTime),new Guid());
            M(8, "X");

            //3.等同於M(5,"A",DateTime.Now,Guid.NewGuid());
            M(5, guid: Guid.NewGuid(), dt: DateTime.Now);

            //4.等同於M(0,"1",default(DateTime),new Guid());
            M(s_n++, s_n++.ToString());

            //5.等同於以下兩行代碼:
            //string t1="2";int t2=3;
            //M(t2,t1,default(DateTime),new Guid());
            M(s: (s_n++).ToString(), x: s_n++);
        }
    }

9.1.1 規則和原則

  • 可為方法、構造器方法和有參屬性(C#索引器)的參數指定預設值。還可為屬於委托定義一部分的參數指定預設值。

  • 有預設值的參數必須放在沒有預設值的所有參數之後。但有一個例外:“參數數組”這種參數必須放在所有參數(包括有預設值的這些)之後,而且數組本身不能有一個預設值。

  • 預設值必須是編譯時能確定的常量值(包括基元類型、枚舉類型,以及能設為null的任何引用類型)。值類型的參數可將預設值設為值類型的實例,並讓它的所有欄位都包含零值。可用defaultnew關鍵字來表達這個意思,兩種語法生成的IL代碼完全一致。

  • 不要重命名參數變數,否則任何調用者以傳參數名的方式傳遞實參,它們的代碼也必須修改。

  • 如果方法從模塊外部調用,更改參數的預設值具有潛在的危險性。call site(發出調用的地方)在它的調用中嵌入預設值。如果以後更改了參數的預設值,但沒有重新編譯包含call site的代碼,它在調用你的方法時就會傳遞舊的預設值。可考慮將預設值 0/null 作為哨兵值使用,從而指出預設行為。例子:

        //不要這樣做:
        private static string MakePath(string filename = "Untitled")
        {
            return string.Format(@"c\{0}.txt", filename);
        }
    
        //而要這樣做:
        private static string MakePath(string filename = null)
        {
            return string.Format(@"C:\{0}.txt", filename ?? "Untitled");
        }
  • 如果參數用 ref 或 out 關鍵字進行了標識,就不能設置預設值。

使用可選或命名參數調用方法時,還要註意以下附加的規則和原則:

  • 實參可按任意順序傳遞,但命名實參只能出現在實參列表的尾部

  • 可按名稱將實參傳給沒有預設值的參數,但所有必須的實參都必須傳遞(無論按位置還是按名稱)。

  • C#不允許省略逗號之間的實參,比如 M(1,,DateTime.Now)。對於有預設值的參數,如果想省略它們的實參,以傳參數名的方式傳遞實參即可。

  • 如果參數要求 ref/out ,為了以傳參數名的方式傳遞實參,請使用下麵這樣的語法:

        //方法聲明:
        private static void M(ref int x) { ...}
    
        //方法調用:
        int a = 5;
        M(x: ref a);
  • C#調用COM組件時,如果是以傳引用的方式傳遞實參,C#還允許省略 ref/out ,進一步簡化編碼。但如果調用的不是COM組件,C#就要求必須向實參應用 ref/out 關鍵字

9.1.2 DefaultParameterValueAttribute 和 OptionalAttribute

9.2 隱式類型的局部變數

  • 不能用 var 聲明方法的參數類型。

  • 不能用 var 聲明類型中的欄位。

  • 不要混淆 dynamicvar 。用 var 聲明局部變數只是一種簡化語法,它要求編譯器根據表達式推斷具體數據類型。 var 關鍵字只能聲明方法內部的局部變數,而 dynamic 關鍵字適用於局部變數、欄位和參數。表達式不能轉型為 var ,但能轉型為 dynamic 。必須顯式初始化用 var 聲明的變數,但無需初始化用 dynamic 聲明的變數。

        private static void ImplicitlyTypedLocalVariables()
        {
            var name = "Jeff";
            ShowVariableType(name); //顯示:System.String

            //var n=null;           //錯誤,不能將null賦給隱式類型的局部變數
            var x = (String)null;   //可以這樣寫,但意義不大
            ShowVariableType(x);    //顯示:System.String

            var numbers = new int[] { 1, 2, 3, 4 };
            ShowVariableType(numbers);  //顯示:System.Int32[]

            //複雜類型能少打一些字
            var collection = new Dictionary<String, Single>() { { "Grant", 4.0f } };

            //顯示:System.Collections.Generic.Dictionary`2[System.String,System.Single]
            ShowVariableType(collection);

            foreach (var item in collection)
            {
                //顯示:System.Collections.Generic.KeyValuePair`2[System.String,System.Single]
                ShowVariableType(item);
            }
        }

        private static void ShowVariableType<T>(T t)
        {
            Console.WriteLine(typeof(T));
        }

9.3 以傳引用的方式向方法傳遞參數

  • CLR 預設所有方法參數都傳值。

  • CLR 允許以傳引用而非傳值的方式傳遞參數。C# 用關鍵字 outref 支持這個功能。

  • CLR 不區分 outref,無論用哪個關鍵字,都會生成相同的 IL 代碼。另外,元數據也幾乎完全一致,只有一個bit除外,它用於記錄聲明方法時指定的是 out 還是 ref

  • C#編譯器是將這兩個關鍵字區別對待的,而且這個區別決定了由哪個方法負責初始化所引用的對象。

  • 使用 out 標記參數的方法,不能讀取該參數的值,而且在返回前必須向這個值寫入。相反,如果用 ref 標記方法,必須在調用方法前初始化參數的值,被調用的方法可以讀取值以及、或者向值寫入

  • 使用 out

        public static void Main()
        {
            int x;                  //x沒有初始化
            GetVal(out x);          //x不必初始化
            Console.WriteLine(x);   //顯示“10”
        }
    
        private static void GetVal(out int v)
        {
            v = 10; //該方法必須初始化v
        }
  • 使用 ref

        public static void Main()
        {
            int x = 5;              //x已經初始化
            GetVal(ref x);          //x必須初始化
            Console.WriteLine(x);   //顯示“15”
        }
    
        private static void GetVal(ref int v)
        {
            v += 10; //該方法可使用v的已初始化的值
        }
  • 不能定義僅在 ref 和 out 上有差別的重載方法。

  • 以傳引用的方式傳給方法的變數,它的類型必須與方法簽名中聲明的類型相同。

        public static void Main()
        {
            string s1 = "Jeffrey";
            string s2 = "Richter";
    
            //錯誤!錯誤!錯誤!
            //Swap(ref s1, ref s2);   
    
            //以傳引用的方式傳遞的變數,
            //必須和方法預期的匹配
            object o1 = s1, o2 = s2;
            Swap(ref o1,ref o2);
    
            //完事後再將object轉型為string
            s1 = (string)o1;
            s2 = (string)o2;
    
            Console.WriteLine(s1);   //顯示“Richter”
            Console.WriteLine(s2);   //顯示“Jeffrey”
        }
    
        private static void Swap(ref object a, ref object b)
        {
            object t = b;
            b = a;
            a = t;
        }
  • 可用泛型來修正上面方法

        public static void Main()
        {
            string s1 = "Jeffrey";
            string s2 = "Richter";
    
            Swap(ref s1, ref s2);
            Console.WriteLine(s1);   //顯示“Richter”
            Console.WriteLine(s2);   //顯示“Jeffrey”
        }
    
        private static void Swap<T>(ref T a, ref T b)
        {
            T t = b;
            b = a;
            a = t;
        }

9.4 向方法傳遞可變數量的參數

  • params 只能應用於方法簽名中的最後一個參數。

  • 這個參數只能標識一維數組(任意類型)。

  • 可為這個參數傳遞 null 值,或傳遞對包含零個元素的一個數組的引用。

  • 調用參數數量可變的方法對性能有所影響(除非顯式傳遞null)。要減少對性能的影響,可考慮定義幾個沒有使用 params 關鍵字的重載版本,如System.String類的Concat方法。

        public static void Main()
        {
            Console.WriteLine(Add(new int[] { 1, 2, 3, 4, 5 }));//顯示“15”
            //或
            Console.WriteLine(Add(1, 2, 3, 4, 5));              //顯示“15”

            //以下兩行都顯示“0”
            Console.WriteLine(Add());       //向Add傳遞 new int[0]
            Console.WriteLine(Add(null));   //向Add傳遞 null :更高效(因為不會分配數組)
        }

        private static int Add(params int[] values)
        {
            // 註意:如果願意,可將values數組傳給其他方法

            int sum = 0;
            if (values != null)
            {
                for (int x = 0; x < values.Length; x++)
                    sum += values[x];
            }
            return sum;
        }

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

  • 聲明方法的參數類型時,應儘量指定最弱的類型,寧願要介面也不要基類。例如,如果要寫方法來處理一組數據項,最好是用介面(比如 IEnumerable<T>)聲明參數,而不要用強數據類型(比如List<T>)或者更強的介面類型(比如ICollection<T>IList<T>):

        //好:方法使用弱參數類型
        public void ManipulateItems<T>(IEnumerable<T> collection){}
    
        //不好:方法使用強參數類型
        public void ManipulateItems<T>(List<T> collection) { }
  • 相反,一般最好是將方法的返回類型聲明為最強的類型(防止受限於特定類型)。例如,方法最好返回FileStream而不是Stream對象:

        //好:方法使用強返回類
        public FileStream OpenFile() { }
    
        //不好:方法使用弱返回類
        public Stream OpenFile() { }
  • 如果想保持一定的靈活性,在將來更改方法返回的東西,請選擇一個較弱的返回類型。

        //靈活:方法使用較弱的返回類型
        public IList<string> GetStringCollection() { }
    
        //不靈活:方法使用較強的返回類型
        public List<string> GetStringCollection() { }

    9.6 常量性


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

-Advertisement-
Play Games
更多相關文章
  • sql查詢某欄位的相同值: SELECT * FROM table WHERE col in (SELECT col FROM table GROUP BY col HAVING COUNT (col) >1); 順帶說一下where和having: select * from tablewhere ...
  • 相比圖形數據的查詢,Neo4j更新圖形數據的速度較慢,通常情況下,Neo4j更新數據的工作流程是:每次數據更新都會執行一次資料庫連接,打開一個事務,在事務中更新數據。當數據量非常大時,這種做法非常耗時,大多數時間耗費在連接資料庫和打開事務上,高效的做法是利用Neo4j提供的參數(Parameter) ...
  • 今天發現線上資料庫主從延遲嚴重: 從庫大量日誌沒有做,當時就想到可能是從庫有事物沒有執行完畢,查看了一下未結束的事物和鎖信息,發現並不是這個原因,查看錯誤日誌: 消息Timeout occurred while waiting for latch: class 'COLUMNSTORE_ROWGRO ...
  • Redis 小白指南(二)- 聊聊五大類型:字元串、散列、列表、集合和有序集合 引言 開篇《Redis 小白指南(一)- 簡介、安裝、GUI 和 C# 驅動介紹》已經介紹了 Redis 的安裝、GUI 和 C# 驅動等基本知識,這一篇主要是梳理一下 Redis 的 5 種類型的信息與指令。 目錄 字 ...
  • 在之前一段時間裡面,我的基類多數使用lock和Hashtable組合實現多線程內緩存的衝突處理,不過有時候使用這兩個搭配並不盡如人意,偶爾還是出現了集合已經加入的異常,對代碼做多方的處理後依然如故,最後採用了.NET 4.0後才引入的ConcurrentDictionary多線程同步字典集合,問題順... ...
  • 本文轉自:https://code.msdn.microsoft.com/Support-Composite-Key-in-d1d53161 he default EntitySetController doesn't support composite keys. So if you have c ...
  • 本文轉自:http://chris.eldredge.io/blog/2014/04/24/Composite-Keys/ In our basic configuration we told the model builder that our entity has a composite key ...
  • 微軟期待聆聽您的聲音 尊敬的開發者,你們好,我們誠邀您參與微軟雲時代開發者調研活動。您的反饋對於微軟至關重要,可以幫助我們為開發者社區提供更多有針對性的內容和雲資源支持,以幫助開發者取得更大成功。 希望您不要錯過本次機會,請將您作為開發者的體驗告訴我們。 為了感謝您的參與,我們將為每一名完成調研的用 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...