首頁  >  文章  >  後端開發  >  詳解可選參數和命名參數實例

詳解可選參數和命名參數實例

零下一度
零下一度原創
2017-06-24 09:46:312443瀏覽

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 参数和返回类型的设计规范

  • 声明方法的参数类型时,应尽量指定最弱的类型,宁愿要接口也不要基类。例如,如果要写方法来处理一组数据项,最好是用接口(比如 IEnumerable8742468051c85b06f0a0af9e3e506b5c)声明参数,而不要用强数据类型(比如List8742468051c85b06f0a0af9e3e506b5c)或者更强的接口类型(比如ICollection8742468051c85b06f0a0af9e3e506b5cIList8742468051c85b06f0a0af9e3e506b5c):

        //好:方法使用弱参数类型
        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() { }

以上是詳解可選參數和命名參數實例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn