C#引用传递

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


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

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关键字:
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用作引用类型。

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方法作如下修改;

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

重新运行程序,发现输出为50.这是因为我们并没有将引用传递给方法?
答案是:不,我们确实发送了引用。引用类型有两个元素---引用和对象。现在,在
调用DoSomething()方法时,我们创建了一个引用副本,它仍然指向同一对象,因此,对对象的改变会影响主程序。而对引用的改变则不会,在方法结束时,消失的只是引用的副本。
1。在使用somePerson.Age属性改变年龄时,我们改变的是对象

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

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

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

### C#引用传递和值传递的区别 在 C# 编程语言中,参数可以通过两种方式传递给方法:按值传递(value passing)和按引用传递(reference passing)。这种差异主要源于 C# 的两数据型——值型(value types)和引用型(reference types),它们的行为决定了如何处理变量及其内容。 #### 值型的传递行为 当一个型被作为参数传递到方法时,默认情况下会执行 **值传递**。这意味着实际上传递给方法的是该值的一个副本,而不是原始对象本身。因此,在方法内部对该参数所做的任何修改都不会影响调用方中的原始值[^1]。 例如: ```csharp void ModifyValue(int number) { number += 10; } int originalNumber = 5; ModifyValue(originalNumber); Console.WriteLine(originalNumber); // 输出仍然是 5 ``` 在这个例子中,`originalNumber` 是一个整数(一种典型的值型)。尽管 `ModifyValue` 方法改变了传入的 `number` 参数,但这一变化并未反映回 `originalNumber` 上,因为它是通过值来传递的。 #### 引用型的传递行为 对于引用型而言,默认情况下的参数传递同样是基于值的方式进行操作;然而这里所说的“值”,实际上是指向堆内存中某个对象实例的引用地址而非具体的数据内容。所以即便是在默认模式下,如果改变这个引用所指向的对象状态,则这些更改能够体现于外部可见范围内[^2]。 考虑下面的例子: ```csharp public class Person { public string Name { get; set; } } void ChangeName(Person person){ person.Name = "John"; } Person p = new Person(); ChangeName(p); Console.WriteLine(p.Name); // 输出 John ``` 在此案例里,虽然我们只是简单地把 `p` 当做一个常规参数送进了函数体内,但由于它属于引用别,故而对其属性的操作直接影响到了原本存在于主程序里的那个实体个体。 值得注意的是,即使在这种情形之下,也仅仅允许调整已存在物件的状态而已,并不意味着可以重新指定新的目标予此参量关联起来。换句话说,要是试图让此形参链接至一不同的实例上去的话,那么这样的变动仅限于是局部性的效果罢了,不会波及到实参身上去[^3]: ```csharp void ReplaceObject(Person person){ person = new Person() { Name="Jane"}; } ReplaceObject(p); Console.WriteLine(p.Name); // 结果依然是 John ``` 以上演示表明,尽管我们可以轻易更动由引用型态变项间接存取得到资料成员之数值,却无法达成替换整个物体的目的除非采用特定关键字如ref或者out加以辅助才行。 #### 使用 ref 和 out 关键字实现真正的引用传递 为了能够在方法间共享同一份可变资源并确保所有地方都能感知到来自其他位置做出的相关改动,C# 提供了两个特殊的关键字:`ref` 和 `out`. 这两者都强制实行完全意义上的参照移交机制,区别在于前者要求事先初始化好待传输的目标容器,而后者的运用则不必如此苛刻条件约束即可成立有效连接关系[^4]. 示例代码如下所示: 利用 `ref` 实现双向通信: ```csharp static void Swap(ref int x, ref int y) { int temp = x; x=y; y=temp; } // 调用前准备阶段 int first=10 , second=20 ; Swap( ref first , ref second ); System.Console.WriteLine($"First:{first}, Second:{second}"); // 打印结果应显示 First:20,Second:10 ``` 借助 `out` 完成单侧赋值任务: ```csharp static bool TryParse(string input,out double result ) { return Double.TryParse(input,System.Globalization.NumberStyles.Any,null,out result ); } double parsedDouble; if(TryParse("123.45",out parsedDouble)) System.Console.WriteLine(parsedDouble.ToString()); else System.Console.WriteLine("Invalid Input"); ``` 综上所述,理解 C# 中值传递引用传递之间的差别至关重要,这不仅有助于编写更加健壮的应用程序逻辑结构设计思路形成过程之中发挥重要作用,而且还能帮助开发者规避许多潜在陷阱从而提升整体开发效率水平高度达到预期标准之上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值