通过引用将引用类型作为参数传递

本文关键字:参数传递 引用类型 引用 | 更新日期: 2023-09-27 17:57:52

好吧,我正试图理解堆叠和堆积以及引用类型与值类型之间的区别,现在我想我对此有了非常基本的理解,但今天我遇到了一个令人难以置信的例子,我想澄清一下:

public class Thing
{
}
public class Animal : Thing
{
    public int Weight;
}
public class Vegetable : Thing
{
    public int Length;
}
public void Go()
{
    Thing x = new Animal();
    Switcharoo(ref x);
    Console.WriteLine(
        "x is Animal    :   "
        + (x is Animal).ToString());
    Console.WriteLine(
        "x is Vegetable :   "
        + (x is Vegetable).ToString());
}
public void Switcharoo(ref Thing pValue)
{
    pValue = new Vegetable();
}

在本例中,x的类型将从Animal变为Vegetable。我不得不承认,我不太明白为什么当我们传递x时,我们不传递对Animal对象所在堆上内存地址的引用吗?在这种情况下,对我来说,Switcharoo唯一要做的事情就是创建一个Vegetable的新实例,该实例在方法执行完成后将被"孤立"。

通过引用将引用类型作为参数传递

通过引用传递时,实际上是在为变量创建别名,因此在Switcharoo中,pValueGo方法中x的别名。因此,分配给CCD_ 12就是分配给CCD13。

Gox的类型是Thing,在运行时,它最初指向类Animal的实例。在调用Switcharoo之后,x转而指向Vegetable类的一个实例。现在无法访问原始Animal实例,可以对其进行收集。

当使用ref时,它是通过引用传递的变量,因此它对引用和值类型(如int)的工作方式相同。在Go中,x将(可能)存在于堆栈上,并且在调用Switcharoo之前,其值将是Animal实例的地址。在Switcharoo内部,pValue是变量x的别名。这可以实现为指向Go中变量的指针,但ref的语义不需要使用指针。

规范描述了ref参数的语义:

5.1.5参考参数

引用参数不会创建新的存储位置。相反引用参数表示与作为函数成员中的参数给定的变量或匿名变量函数调用。因此,参考参数的值总是与基础变量相同。

如果通过引用传递参数,则传递对原始值的引用,并且可以更改该原始值。如果这个值是引用类型的,那么方法得到的就是对引用的引用!

这允许该方法更改原始引用。Switcharoo所做的是将新的Vegetable分配给x


如果参数不是通过引用获得的,则该方法将获得原始值的副本。但是,如果参数类型是引用类型,则该方法仍然可以更改被引用的原始对象的属性,但不能更改原始引用,因为它只获得原始引用的副本。然后,该方法只能更改引用的本地副本。


参考

   Thing x
  +-------+          +--------+
  |   O---|--------->| object |
  +-------+          +--------+
        ^
        |
public void Switcharoo(ref Thing pValue)
{       |
    +---|---+
    |   O   | pValue
    +-------+
}

按值

   Thing x  
  +-------+          +--------+  
  |   O---|--------->| object |
  +-------+          +--------+
                         ^
                         |
public void Switcharoo(Thing pValue)
{    pValue              |
    +-------+            |
    |   O---|------------+ 
    +-------+
}

当您使用ref传递引用类型时,意味着您可以更改引用本身。这意味着在您的情况下,您将创建一个新的Vegetable,并将对它的引用放在x中。

现在,在没有ref的情况下,Switcharoo内的局部x不同于Go内的外部x,但这随着ref而变化。现在它们具有相同的价值。

从概念上讲,您正在做的是发送对x的引用,而CCD_43本身就是对Animal的引用。当您更改它时,x将保留对新Vegetable的引用。


按值传递参考类型:

  1. x=新动画()->x=0x0001。(0x0001是动物的记忆位置)
  2. Switcharoo()->x=0x0001,p值=0x0001
  3. pValue=new Vegetable()->x=0x0001和pValue=0x0002(0x0002是Vegetable的内存位置)
  4. Switcharoo()结束->x=0x0001

通过引用的引用类型:

  1. x=新动画()->x=0x0001
  2. Switcharoo()->x=0x0001,p值=0x0001
  3. pValue=new Vegetable()->x=0x0002和pValue=0x0002(因为x和pValue是保存0x0002的同一内存位置的两个名称)
  4. Switcharoo()结束->x=0x0002