您好,欢迎访问一九零五行业门户网

详解可选参数和命名参数实例

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的任何引用类型)。值类型的参数可将默认值设为值类型的实例,并让它的所有字段都包含零值。可用default或new关键字来表达这个意思,两种语法生成的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 和 optionalattribute9.2 隐式类型的局部变量不能用 var 声明方法的参数类型。
不能用 var 声明类型中的字段。
不要混淆 dynamic 和 var 。用 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# 用关键字 out 或 ref 支持这个功能。
clr 不区分 out 和 ref,无论用哪个关键字,都会生成相同的 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() { }
以上就是详解可选参数和命名参数实例的详细内容。
其它类似信息

推荐信息