您的位置:首页 > 其它

传值,传址,引用,ref,out

2016-01-04 21:55 369 查看

c# 的传递参数值传递与传递引用的区别,ref与out区别

值传递 

C#默认都是值传递的,就是复制变量的一个副本传递给方法,所以在退出方法后,对变量的修改无效。

但是要注意,当传递是引用类型时,因为引用类型是一个引用的地址,所以修改引用地址指向的对象时,一样会修改对象的值,这种现象不能当做引用传递

引用传递

引用传递是对象本身传递给方法,当在方法中对对象做修改时,退出方法后修改是有效的,在C#中引用传递需要在参数类型前加关键字 ref ,但是ref 的参数变量在使用前必须被初使化,可有时通过引用传递的变量初值是没意义的,这容易产生混淆,这时在C#中有另一关键字 out ,out 与 ref 关键字使用时效果一样,但是out关键字对变量可以不初使化,但是out关键字的变量在方法中必须给分配一个值,否则编译会不通过.

一、传递参数
既可以通过值也可以通过引用传递参数。通过引用传递参数允许函数成员(方法、属性、索引器、运算符和构造函数)更改参数的值,并保持该更改。

二、传递值类型参数
值类型变量直接包含其数据,这与引用类型变量不同,后者包含对其数据的引用。因此,向方法传递值类型变量意味着向方法传递变量的一个副本。方法内发生的对参数的更改对该变量中存储的原始数据无任何影响。如果希望所调用的方法更改参数的值,必须使用 ref 或 out 关键字通过引用传递该参数。为了简单起见,下面的示例使用 ref。

学过C#的人都知道,通过或通过引用,值类型和引用类型都可以作为方法参数传递。在C#中,不管是值类型或者是引用类型,所有方法参数在默认情况下是通过值传递的。

1)通过值传递值类型
在通过值传递作为方法参数的变量时,传递给方法的是数据副本。在方法中对该数据的任何修改都不会对初始值有任何影响
C#如下代码:

[csharp]
view plaincopyprint?

using System;  
class MyExecutableClass  
{  
static void Main(string[] args)  
{  
int value=50;  
DoSometing(value);  
Console.WriteLine(value);  
}  
  
static void DoSomething(int parameter)  
{  
parameter=100;  
}  
}  

using System;
class MyExecutableClass
{
static void Main(string[] args)
{
int value=50;
DoSometing(value);
Console.WriteLine(value);
}

static void DoSomething(int parameter)
{
parameter=100;
}
}

程序的输出为50.也许你会感到奇怪,为什么不是100呢?因为变量value是通过值而不是引用传递的。我们不需要添加任何特殊的关键字,而是依赖于C#的默认行为,通过值传递值类型。

2)通过引用传递值类型
通过引用传递值类型,也就是说传递值(变量)的引用。如果传递引用的话,那么无论在程序的什么地方作改变的话(可能是在另一个方法、属性中,甚至是另一个对象中),都会改变使用改引用的值。对方法中任何参数的改变都将影响方法的返回值。
在C#中,通过引用传递是通过ref关键字实现的,必须按如下所示将ref关键字添加到方法定义中:
static void DoSomething(ref int parameter)
传递参数时,必须使用ref关键字。
DoSomething(ref value)
下面的代码演示了如何对值类型使用ref关键字:

[csharp]
view plaincopyprint?

using System;  
class MyExecutableClass  
{  
static void Main(string[] args)  
{  
int value=50;  
DoSomething(ref value);  
Console.WriteLine(value);  
}  
static void DoSomething(ref int parameter)  
{  
parameter=100;  
}  
}  

using System;
class MyExecutableClass
{
static void Main(string[] args)
{
int value=50;
DoSomething(ref value);
Console.WriteLine(value);
}
static void DoSomething(ref int parameter)
{
parameter=100;
}
}
结果正如你所料,输出为100.

3)通过值传递引用类型
一般来说,通过值传递意味着传递存储在栈中的值。对于引用类型来说,存储在栈上的值表示对

内存中对象实际位置的引用。因此,如果通过值传递引用类型,就意味着传递的是对象的引用(它的堆栈)

.使用该引用作的改变最终会改变堆中的同一对象。
通过值传递引用类型不像通过值传递值类型---它更像通过引用传递值类型。在如下代码中,

我们将Person用作引用类型。

[csharp]
view plaincopyprint?

using System;  
class MyExecutableClass  
{  
static void Main(string[] args)  
{  
Person person=new Person(50);  
DoSomething(person);  
Console.WriteLine(person.Age);  
}  
static void DoSomething(Person somePerson)  
{  
somePerson.Age=100;   
}  
}  
class Person  
{  
public int Age;  
public Person(int Age);  
{  
this.Age=Age;  
}  
}   

using System;
class MyExecutableClass
{
static void Main(string[] args)
{
Person person=new Person(50);
DoSomething(person);
Console.WriteLine(person.Age);
}
static void DoSomething(Person somePerson)
{
somePerson.Age=100;
}
}
class Person
{
public int Age;
public Person(int Age);
{
this.Age=Age;
}
}
运行程序,可以发现输出值为100.

如果对DoSometing方法作如下修改;

[csharp]
view plaincopyprint?

static void DoSomething(Person somePerson)  
{  
somePeron=new Person(100);  
}  

static void DoSomething(Person somePerson)
{
somePeron=new Person(100);
}


重新运行程序,发现输出为50.这是因为我们并没有将引用传递给方法?
答案是:不,我们确实发送了引用。引用类型有两个元素---引用和对象。现在,在

调用DoSomething()方法时,我们创建了一个引用副本,它仍然指向同一对象,因此,对对象的改变会影响主程序。而对引用的改变则不会,在方法结束时,消失的只是引用的副本。
1。在使用somePerson.Age属性改变年龄时,我们改变的是对象

2。但接下来是创建一个新对象,改变引用来指向它---对引用的改变将会丢失。
应该怎么做呢?方案就是通过引用传递引用类型,那样作的话,如果改变somePerson所存储的引用,那么另一个“父”引用会自动更新。听起来很混乱,下面再讨论。

4) 通过引用传递引用类型
我们知道,在通过值传递引用类型时,我们传递对内存中对象位置的引用。而通过引用传递引用类型时,我们将传递引用的引用。
正如我们所看到的,通过值传递引用类型并不适合于所有情况---特别是需要改变引用以指向新对象时。
下面例子就是说明通过引用传递就很有用。

[html]
view plaincopyprint?

<pre class="csharp" name="code"></pre><pre class="csharp" name="code">using System;  
class MyExecutableClass  
{  
static void Main(string[] args)  
{  
Person person=new Person(50);  
DoSometing(ref person);  
Console.WriteLine(person.Age);  
}  
static void DoSometing(ref Person somePerson)  
{  
somePerson=new Person(100);  
}</pre><pre class="csharp" name="code">} </pre>  
<pre></pre>  

<div class="dp-highlighter bg_csharp"><div class="bar"><div class="tools"><strong>[csharp]</strong> <a target=_blank title="view plain" class="ViewSource" href="http://blog.csdn.net/lllljz/article/details/7625605#">view plain</a><a target=_blank title="copy" class="CopyToClipboard" href="http://blog.csdn.net/lllljz/article/details/7625605#">copy</a><a target=_blank title="print" class="PrintSource" href="http://blog.csdn.net/lllljz/article/details/7625605#">print</a><a target=_blank title="?" class="About" href="http://blog.csdn.net/lllljz/article/details/7625605#">?</a></div></div><ol class="dp-c"><li class="alt"><span><span>  </span></span></li></ol></div><pre class="csharp" style="display: none;" name="code">


[csharp]
view plaincopyprint?

using System;  
class MyExecutableClass  
{  
static void Main(string[] args)  
{  
Person person=new Person(50);  
DoSometing(ref person);  
Console.WriteLine(person.Age);  
}  
static void DoSometing(ref Person somePerson)  
{  
somePerson=new Person(100);  
}  

using System;
class MyExecutableClass
{
static void Main(string[] args)
{
Person person=new Person(50);
DoSometing(ref person);
Console.WriteLine(person.Age);
}
static void DoSometing(ref Person somePerson)
{
somePerson=new Person(100);
}


[csharp]
view plaincopyprint?

}   

}




这次输出为100;person变量实际上对堆上Person对象的引用。在调用DoSomething()时,编译器创建了对Person引用的引用(而不是对Person对象的引用).在DoSometing()方法中,somePerson是Person引用的引用,而Person引用堆上的对象。然而,DoSomething()知道值是通过引用传递的,因此对somePerson的任何改变实际上是改变了person。结果就是somePerson的行为就好像它是person引用,而不是其副本。


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: