好吧,我正试图围绕堆叠和堆积以及引用类型与值类型之间的差异而不是什么,现在我认为我对此有了一个非常基本的理解,但今天我遇到了一个令人难以置信的例子我想澄清一下:
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
的新实例,在该方法执行完成后将成为“孤立”的实例。
答案 0 :(得分:2)
当您通过引用传递时,您实际上正在为变量创建别名,因此在Switcharoo
中,pValue
是x
方法中Go
的别名。因此,分配给pValue
是对x
的分配。
x
中Go
的类型为Thing
,并且在运行时,它最初指向类Animal
的实例。调用Switcharoo
后,x
将指向Vegetable
类的实例。原始Animal
实例现在无法访问,可以收集。
使用ref
时,它是通过引用传递的变量,因此它对引用和值类型(如int
)的工作方式相同。在Go
中,x
将(可能)存在于堆栈中,在调用Switcharoo
之前,其值将是Animal
实例的地址。在Switcharoo
内,pValue
是变量x的别名。这可以实现为Go
中变量的指针,但ref的语义不需要使用指针。
规范描述了ref
参数的语义:
5.1.5参考参数
参考参数不会创建新的存储位置。代替, 引用参数表示与之相同的存储位置 变量作为函数成员或匿名中的参数给出 函数调用。因此,参考参数的值总是如此 与基础变量相同。
答案 1 :(得分:2)
如果通过引用传递参数,则传递对原始值的引用,并且可以更改此原始值。如果此值是引用类型,那么方法获得的是对引用的引用!
这允许该方法更改原始引用。 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---|------------+
+-------+
}
答案 2 :(得分:1)
使用ref传递引用类型时,意味着您可以更改引用本身。这意味着在您的情况下,您将创建一个新的Vegetable
并将其引用放在x
中。
现在没有ref
,x
内的Switcharoo
与x
内的Go
不同,但ref
会更改x
。现在它们具有相同的价值。
从概念上讲,您正在做的是发送对Animal
的引用,x
本身就是对Vegetable
的引用。当您进行更改时,{{1}}会保留对您的新{{1}}。
按值传递参考类型:
通过参考传递参考类型: