所以我正在深入阅读Jon Skeet的C#并且遇到了一些神话,比如参考类型总是通过ref传递,所以我决定自己做一个小实验。
正如您在下面的代码中看到的,我有一个带有一个属性的简单Car类,当调用构造函数时,该属性初始化为500。我还有NullIt函数,它为参数值赋值null,以及一个只改变Speed属性值的SpeedUp方法。
检查你可以看到的主方法我实例化一个Car对象,然后我将对象传递给静态SpeedUp方法,并且Speed值更改为1000但是当我将它传递给静态NullIt方法时,对象保持不变。从这里我唯一可以假设的是,对象是通过值传递的,字段/属性是通过引用传递的。这是对的吗?
我知道如果我使用ref关键字传递它将返回null。
class Program
{
static void Main(string[] args)
{
Car c = new Car();
Car.SpeedUP(c);
Car.NullIt(c);
}
class Car
{
public int Speed { get; set; }
public Car() { Speed = 500; }
public static void SpeedUP(Car c)
{
c.Speed = 1000;
}
public static void NullIt(Car c)
{
c = null;
}
}
}
答案 0 :(得分:7)
从这里我唯一可以假设的是对象被传递 value和fields / properties通过引用传递。这是 正确?
不是真的。 对象的地址按值传递。
所以当你这样做时:
Car.SpeedUP(c);
现在,调用者中方法SpeedUp
的参数和字段c
都指向内存中的相同位置。因此改变财产的作用。
但是你的电话:
Car.NullIt(c);
您的方法参数c
和来电者的c
都指向同一位置。但是,由于您为参数c
指定了null,现在它没有指向任何内存位置,但原始/调用者的c
仍然指向相同的内存位置。
请考虑以下事项:
当您将参数传递给您的方法时,内存中的两个引用将指向相同的地址,如:
但是当你指定null
时,它不会改变另一个引用。
第一个引用(在调用方中)仍指向同一位置,只有method参数现在指向null
。
答案 1 :(得分:0)
当您致电NullIt()
时,您正在传递对Car
实例的引用的值。然后,将此值更改为null。但是,该值的原始副本保持不变。传递int
的值的方式完全相同 - 您可以修改本地副本而不影响“原始”。
如果要将其更改为NullIt(ref Car c)
,则会传递对引用的引用,因此将其设置为null会将引用的原始值设置为null。最后一部分可能有点令人费解,但很少有必要(如果有的话)你不需要太担心它。
答案 2 :(得分:0)
由此我唯一可以假设的是,对象是按值传递的,而fields / properties是通过引用传递的。这是对的吗?
虽然观察到的效果可能如下所示,但您描述它的方式有一些不正确的含义:字段/属性不是“通过引用传递”,而是存在对每个字段/属性的引用。更确切地说,对象的引用是按值传递的。
这就是为什么通过访问对象的任何成员或对象本身,您正在访问传递给方法的同一个实例,而不是它的副本。
但是,变量c
本身是一个引用,该引用按值传递。这就是为什么在您的方法中,您无法通过指定新值来更改该引用,并期望变量c
本身现在具有新值(在您的情况下为null
)。
答案 3 :(得分:0)
这有点令人困惑,因为 reference 这个词被重载(有两个微妙的不同含义)。
将类型描述为值类型或引用类型时。这意味着一件事, 该类型对象的状态数据是否存储在堆栈上(这是方法可以访问的内存的一部分),或者是否存储在另一部分内存中,称为堆,然后只有堆的那一部分的地址存储在堆栈中,允许代码仅间接访问对象。
当描述参数值是通过值还是通过引用传递给方法时,otoh,它意味着不同的东西。它表示参数的实际值是否[复制并]传递给方法,或者是否传递了参数值的内存槽的地址。
所以你实际上可以在这里有四种组合: