代码改变世界

c#进阶params可变个数的参数

2010-12-28 02:55  撞破南墙  阅读(3808)  评论(6编辑  收藏  举报

 

目录

1 简单使用

2 可变个数的参数params 的原理

3 性能分析

4 较佳实践建议


  系列索引

C#功能派的进阶2--类型的那些事


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; 
}

 明显他可以这样使用 

public static void Main() { 
// Displays "15" 
Console.WriteLine(Add(new Int32[] { 12345 } )); 
}

 但是这样有点丑,也许这样会漂亮点。params的特性

public static void Main() { 
// Displays "15" 
Console.WriteLine(Add(12345)); 
}

      但是哦呼,他真的可以运行。这是params参数的特权,去除params之后就会报错,因为他就变成了3个参数的函数了。是为了消除二义性,将不能再定义不带params 的函数。

2可变个数的参数params 的原理

在想params是如何实现的时候,我猜测params是编译器的语法糖在IL中不存在。打开reflactor来尝试验证

public static void Main() { 
            DisplayTypes(
134); 
            DisplayTypes(
new Object[] { 134 }); 
            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[] { (int1, (int3, (int4 });
    DisplayTypes(
new object[] { (int1, (int3, (int4 });
    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); 
}

 

4参数和返回值编写的较好实践:

 

1参数和返回值尽量使用接口和基类

显而易见,可以适用被更多种(派生)类型。

 

2常量性(const-ness)的对象和参数

类的常量性其实是自欺欺人,如前所述(我之前的一篇文章)我们虽然不能改变类的地址,但可以改变地址的值。这样的话C++等实现的常量性类,是在自欺欺人吗?其实我们可以在设计类的时候就不让他改变就得了?(private set),他也有很多不好的地方如 使得执行的时候需要检查是否是常量(不可改变)而降低性能。