9参数

c#中,关于参数有 ref,out,params等修饰关键字,同时,也允许参数具有默认值,通过参数名称进行调用。

如果一个方法有多个参数,并且有的参数具有默认值,那么具有默认值的参数应当在最后【也即,具有默认的参数右边不能有没有默认值的参数存在——–可变参数数组是个例外】。
类似:

public void func(int i,j = 5,k = 10);

对于func的调用:

func(0);//只是对i赋值,j取默认值5,k取默认值10
func(1,2);//对i,j分别赋值为i = 1,j = 2,k取默认值10
func(1,2,3);//三者分别赋值


还可以使用参数名进行赋值【从而允许乱序】
func(0,k:50);//i = 0,k = 50,j取默认值5

对于有默认值的参数,编译器在发现某个参数有默认值时,会为其加上DefaultParamValue和Optional特性【System.Runtime.InteropService命名空间】进行标注。

要注意的一点是:
有默认值的参数,值必须是编译时期可以确定的常量【值类型虽然用new但是是个例外,详情可以参看上篇介绍】

可变参数数组,也就是使用params关键字来修饰的数组参数。
首先引入例子:

public int add(int[] a){
    //省略检查有效性

    int sum = 0;
    foreach(var i in a){
      sum += i;
    }
}

//调用端:

add(new int[]{1,2,3});

我如果希望这样写呢:
add(1,2,3);
此时就需要对add的参数进行params修饰了。

为了完整,接下来说说,ref和out修饰符。这两个修饰符本质区别是: 由【调用者/被调用者】来初始化传递参数。
ref:ref修饰的参数,要求调用者这边必须初始化,被调用者可以对参数进行读取和写入。
out:out修饰的参数,被调用者这边不需要初始化【也没有必要和意义】,被调用者不允许对参数进行读取【这就是为什么调用者初始化没有意义的原因】,并且必须对参数进行写入。

这里对于值类型通过ref,out修饰,不做过多说明【其实类似c++语言中的指针,进行传地址(地址传值)传递】。说说对于引用类型的修饰,同时这个例子还有一点其他需要注意的地方。

//首先定义了一个swap函数
public void swap(ref Object lhs,ref Object rhs){
    Object o = lhs;
    lhs = rhs;
    rhs = o;
}

//接下来
string s1 = "hello";
string s2 = "world";

//Main
swap(ref s1,ref s2);

有问题吗?这段代码是无法通过编译的。编译器抱怨:swap(ref object, ref object)”最匹配的重载方法具有一些无效参数。什么意思?编译器要求对于这种方式,必须使用完全一致的类型。为什么要这样子呢?看个例子:

public void func(ref object o){
    ...
    o = new List<string>();
}

//Main

string s = "xiaolu";
func(ref s);//ooh...

问题很明显了,感谢编译器不允许这样的代码通过。否则,s明明是个string,结果到了func中,转为了object,然后…变成了List<string>
要想让上面的代码可以运行,可以这样做【你也可以将swap的参数类型改成string,从而精确匹配了。这里不再描述】:

 public void swap(ref object lhs,ref object rhs)
        {
            object o = lhs;
            lhs = rhs;
            rhs = o;
        }

        public static void Main(String[] args)
        {
            string s1 = "hello";
            string s2 = "world";

            object o1 = s1;
            object o2 = s2;

            new Demo().swap(ref o1, ref o2);

            s1 = o1 as string;
            s2 = o2 as string;

            System.Console.WriteLine("s1:{0},s2:{1}", s1, s2);

        }

或者认为转型太过麻烦,可以使用泛型方式:

public void swap<TELE>(ref TELE lhs,ref TELE rhs)
        {
            TELE o = lhs;
            lhs = rhs;
            rhs = o;
        }

        public static void Main(String[] args)
        {
            string s1 = "hello";
            string s2 = "world";

            new Demo().swap(ref s1, ref s2);

            System.Console.WriteLine("s1:{0},s2:{1}", s1, s2);

        }

个人更喜欢这个- - 方便。

其实,对于ref和out不管作用于引用还是值类型,简单可以认为,只要有这两个修饰符的修饰,就是一次取地址。这样理解就应该没有什么疑惑的地方【对于ref string,可以认为传递的是指针的指针,从而可以进行交换。达到我们的想要效果,因为我觉得这里还是传值,如果是可以传址(c++中的引用),那么就不需要ref或者out修饰,直接可以修改地址,而这里显然不行】

 public void swap<TELE>( TELE lhs, TELE rhs)
        {
            TELE o = lhs;
            lhs = rhs;
            rhs = o;

            System.Console.WriteLine("s1:{0},s2:{1}", lhs, rhs);//这里修改了,但是对于Main中没有影响,显然这里是一个地址的拷贝而已。
        }

        public static void Main(String[] args)
        {
            string s1 = "hello";
            string s2 = "world";

            new Demo().swap( s1,  s2);

            System.Console.WriteLine("s1:{0},s2:{1}", s1, s2);

        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值