引用类型作为参数

时间:2014-05-06 14:53:27

标签: c#

所以我正在深入阅读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;
        }
    }
}

4 个答案:

答案 0 :(得分:7)

  

从这里我唯一可以假设的是对象被传递   value和fields / properties通过引用传递。这是   正确?

不是真的。 对象的地址按值传递。

所以当你这样做时:

Car.SpeedUP(c);

现在,调用者中方法SpeedUp的参数和字段c都指向内存中的相同位置。因此改变财产的作用。

但是你的电话:

Car.NullIt(c);

您的方法参数c和来电者的c都指向同一位置。但是,由于您为参数c指定了null,现在它没有指向任何内存位置,但原始/调用者的c仍然指向相同的内存位置。

请考虑以下事项:

当您将参数传递给您的方法时,内存中的两个引用将指向相同的地址,如:

enter image description here

但是当你指定null时,它不会改变另一个引用。

enter image description here

第一个引用(在调用方中)仍指向同一位置,只有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,它意味着不同的东西。它表示参数的实际值是否[复制并]传递给方法,或者是否传递了参数值的内存槽的地址。

所以你实际上可以在这里有四种组合:

  1. 按值传递值类型 - 复制并传递值 方法。该方法无法更改源值。
  2. 按值传递引用类型。引用类型的地址 (在堆上)被复制并传递。方法不能 更改源变量中的地址,但它可以更改 地址所指的HEAP数据。
  3. 通过引用传递值类型。该方法获取的地址 源对象,(在堆栈上)并且可以更改该源值。
  4. 通过引用传递引用类型。该方法获取地址 包含对象地址的变量(在堆栈上) 本身(在堆上)。该方法可以更改源中的数据 对象,也可以改变对象(在头上)的来源 可变点())