从C/C++到C#之二 函数

函数参数引用传递与值传递:
C#对函数参数做了更明确的标示,使得程序更加清晰可读,通过使用in和out关键字说明了参数的作用,in修饰的参数在形参和实参结合的时候会生成副本,这样对形参的修改就不会对实参产生影响(副作用),因为这个函数内部使用不是实参而是生成的那个副本。out关键字函数对形参的修改会对实参产生副作用的,这样out的意思就是说我要用这个参数作为返回值。这点和C++引用传递的语义是类似的,函数对参数的修改会反映在实参上,这样我就可以得到最终变化的结果,其语义就是return的作用一样。没有in,out修饰默认是in.
[code]
public void InternalMagic(int x)
{
x+=100;
}

int i=100;
InternalMagic(i);
Console.WriteLine(i);
[/code]
输出是// Output will be '100' -- easy, little magic !-_-
[code]
public void InternalMagic(out int x)
{
x+=100;
}

int i=100;
InternalMagic(i);
Console.WriteLine(i);
输出是// Output will be '200' -- easy, little magic !-_-
[/code]
这两个例子我们看到了in和out的区别


传递值和引用的区别:
上次提到过引用类型和值类型,引用类型其实就是C++的指针类型的类似,是通过一个间接的东西持有一个值(对象),如StringBuffer buffer = new StringBuffer();这个buffer是引用类型,他得特点是复制不会拷贝原来的对象:
[code]
StringBuilder first = new StringBuilder(“Hello,”);
StringBuilder second = first;
[/code]
这段代码其实first和second都指向同一段内存空间,里面的内容是”Hello,”.
[code]
StringBuilder first = new StringBuilder(“Hello,”);
StringBuilder second = first;
first.Append ("world");
first = null;
Console.WriteLine (second);

[/code]
结果是Hello,world.我们对first的修改会影响到second.
知道这些后我们看看传递值和引用的问题吧,这个问题相当让人迷惑:
[code]
void Foo (StringBuilder x)
{
x = null;
}
StringBuilder y = new StringBuilder();
y.Append ("hello");
Foo (y);
Console.WriteLine (y==null);
[/code]
打印出false,我们前面说了in类型的参数传递函数对形参x的修改不会影响实参y的,所以
y!=null.
我们再看一个例子:
[code]
void Foo (StringBuilder x)
{
x.Append (" world");
}

StringBuilder y = new StringBuilder();
y.Append ("hello");
Foo (y);
Console.WriteLine (y);
[/code]
结果会使hello world.是不是很让人迷惑啊?修改x怎么y也变了呢,和上面的不是矛盾了么?我们前面已经说了引用类型和值类型的区别了,这个问题就会容易理解一些了:
实参y作为参数传递的时候的确产生了副本,但实参y是个引用类型,生成了一个引用的副本,不妨叫做y1,StringBuffer对象”hello”不会产生副本,这样y1让去操作StringBuffer对象”hello”,append(“ world”)当然会影响实参y了所指的同一个StringBuffer对象”hello”了,因为他们指向同一个StringBuffer对象”hello”。而上面的例子,x=null.这个只是修改了副本的指向一个null的东东,对实参y没有影响,还是指向原来的。
使用ref修饰:
[code]
void Foo (ref StringBuilder x)
{
x = null;
}

...

StringBuilder y = new StringBuilder();
y.Append ("hello");
Foo (ref y);
Console.WriteLine (y==null);
[/code]
这次打印出True了?ref修饰起什么作用的,这个东西和C++的&作用一样,引用传递,
参数传递的时候不会产生副本,对形参的修改会反映在实参上。所以x=null;导致了y也为
null了。
ref修饰和out修饰的区别:
ref和out修饰的效果是一样的,都是参数传递的时候不产生副本,但是他们的目的是不一样的,out的目的是这个参数作为返回值用,而ref的目的是形参不生成副本。
[code]
void Foo (out int x)
{
// 不能去读x,int a = x 是不允许的,x默认的语义是没有赋值过

//必须赋值之后才能正常结束。
x = 10;

// 赋值之后才能去读。
int a = x;
}

// y没有被初始化
int y;

// y作为out参数传递,没有被初始化
Foo (out y);

// 现在y已经有值了,他和返回值的语义相同,所以我们叫做out参数
Console.WriteLine (y);
[/code]
输出为10
从上面的例子我们可以看到三点不同:
1、out修饰的实参传递之前是不需要被复制的
2、out参数被认为是没有被初始化的,所以你不能在他被复制之前去读它
3、out参数函数结束之前需要被赋值。

C#需要传递数组的时候需要使用params,这点和C++没有params修饰的效果是一样的:
[code]
public class Test
{
public static void Main () {
Console.WriteLine (add (1, 2, 3, 4).ToString());
}

public static int add (params int[] array) {
int sum = 0;
foreach (int i in array)
sum += i;
return sum;
}
}

[/code]

函数重载:
函数重载就是一个函数的名字相同,它的参数和修饰不同:
和C/C++一样返回值是不能作为重载的依据的。
我们看看C#的一些特殊的修饰:
Fun(int)和Fun(ref int) or Fun(out int)
是可以共存的,也就是说他们是重载的
但Fun(ref int) 和 Fun(out int)是不能共存的,他们不是重载的
判断重载的依据是:
函数名相同,调用这个函数的时候不会有歧义(不知道是调用具有这个函数名中的哪个?)
也就是函数名相同并且可以在一个作用于内共存的函数。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值