詳解選擇性參數和具名引數執行個體

來源:互聯網
上載者:User

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() { }
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.