目录
1 简单使用
2 可变个数的参数params 的原理
3 性能分析
4 较佳实践建议
系列索引
1简单使用
static
Int32 Add(
params
Int32[] values) {
// NOTE: it is possible to pass the 'values'
// array to other methods if you want to.
Int32 sum = 0 ;
if (values != null ) {
for (Int32 x = 0 ; x < values.Length; x ++ )
sum += values[x];
}
return sum;
}
// NOTE: it is possible to pass the 'values'
// array to other methods if you want to.
Int32 sum = 0 ;
if (values != null ) {
for (Int32 x = 0 ; x < values.Length; x ++ )
sum += values[x];
}
return sum;
}
明显他可以这样使用
public
static
void
Main() {
// Displays "15"
Console.WriteLine(Add( new Int32[] { 1 , 2 , 3 , 4 , 5 } ));
}
// Displays "15"
Console.WriteLine(Add( new Int32[] { 1 , 2 , 3 , 4 , 5 } ));
}
但是这样有点丑,也许这样会漂亮点。params的特性
public
static
void
Main() {
// Displays "15"
Console.WriteLine(Add( 1 , 2 , 3 , 4 , 5 ));
}
// Displays "15"
Console.WriteLine(Add( 1 , 2 , 3 , 4 , 5 ));
}
但是哦呼,他真的可以运行。这是params参数的特权,去除params之后就会报错,因为他就变成了3个参数的函数了。是为了消除二义性,将不能再定义不带params 的函数。
2可变个数的参数params 的原理
在想params是如何实现的时候,我猜测params是编译器的语法糖在IL中不存在。打开reflactor来尝试验证
public
static
void
Main() {
DisplayTypes( 1 , 3 , 4 );
DisplayTypes( new Object[] { 1 , 3 , 4 });
Console.Read();
}
private static void DisplayTypes( params Object[] objects) {
if (objects != null ) {
foreach (Object o in objects)
Console.WriteLine(o.GetType());
}
}
DisplayTypes( 1 , 3 , 4 );
DisplayTypes( new Object[] { 1 , 3 , 4 });
Console.Read();
}
private static void DisplayTypes( params Object[] objects) {
if (objects != null ) {
foreach (Object o in objects)
Console.WriteLine(o.GetType());
}
}
对应的Main方法
public
static
void
Main()
{
object [] CS$ 0 $ 0000 ;
DisplayTypes( new object [] { ( int ) 1 , ( int ) 3 , ( int ) 4 });
DisplayTypes( new object [] { ( int ) 1 , ( int ) 3 , ( int ) 4 });
Console.Read();
return ;
}
{
object [] CS$ 0 $ 0000 ;
DisplayTypes( new object [] { ( int ) 1 , ( int ) 3 , ( int ) 4 });
DisplayTypes( new object [] { ( int ) 1 , ( int ) 3 , ( int ) 4 });
Console.Read();
return ;
}
果然不出所料。
另外补充来自你必须知道的.NET中的分析
param关键字的实质是:param是定制特性ParamArrayAttribute的缩写(关于定制特性的详细论述请参见
第三回:历史纠葛:特性和属性),该特性用于指示编译器的执行过程大概可以简化为:编译器检查到方法调用时,首先调用不包含ParamArrayAttribute特性的方法,如果存在这种方法就施行调用,如果不存在才调用包含ParamArrayAttribute特性的方法,同时应用方法中的元素来填充一个数组,同时将该数组作为参数传入调用的方法体。总之就是param就是提示编译器实现对参数进行数组封装,将可变数目的控制由编译器来完成, 实质是:
void XXXX( [ParamArrayAttribute] int[] ages){...}
3性能分析
其实质其实构造一个 array 在编译时确定其长度来 承载可变个数的参数.但性能上并不划算:因为需要额外构造一个array。
1在堆上分配内存
2初始化其元素
3最后还得被回收。
所以出于性能更好的做法是重载。我们可以看String 中Concat的做法。
public
sealed
class
String : Object, ... {
public static string Concat( object arg0);
public static string Concat( object arg0, object arg1);
public static string Concat( object arg0, object arg1, object arg2);
public static string Concat( params object [] args);
public static string Concat( string str0, string str1);
public static string Concat( string str0, string str1, string str2);
public static string Concat( string str0, string str1, string str2, string str3);
public static string Concat( params string [] values);
}
public static string Concat( object arg0);
public static string Concat( object arg0, object arg1);
public static string Concat( object arg0, object arg1, object arg2);
public static string Concat( params object [] args);
public static string Concat( string str0, string str1);
public static string Concat( string str0, string str1, string str2);
public static string Concat( string str0, string str1, string str2, string str3);
public static string Concat( params string [] values);
}
4参数和返回值编写的较好实践:
1参数和返回值尽量使用接口和基类
显而易见,可以适用被更多种(派生)类型。
2常量性(const-ness)的对象和参数
类的常量性其实是自欺欺人,如前所述(我之前的一篇文章)我们虽然不能改变类的地址,但可以改变地址的值。这样的话C++等实现的常量性类,是在自欺欺人吗?其实我们可以在设计类的时候就不让他改变就得了?(private set),他也有很多不好的地方如 使得执行的时候需要检查是否是常量(不可改变)而降低性能。