好的,在你跳起来之前,你需要了解与pass-by-ref相比的传值。您可能不同意传值的这个定义,但这仅仅是语义,因为真正的问题是堆栈分配和堆分配之间发生的事情。
Pass-by-value:复制要传递的对象,并将对象的副本作为参数提交给函数(好的,OO纯粹是你想把它称为“方法” - 语义!)。因此,在函数结束/返回时,无论对象的副本做了什么,都不会修改原始对象。
所以Java(也可能是C#)是一种按值传递的语言。有些人认为它们是pass-by-ref,但实际上传递的args是引用。因此,引用的副本将传递给函数。也就是说,引用是按值传递arg,因为原始引用在函数的结束/返回时不会更改。
现在我们已经解决了这个问题,接受了我的价值传递,这就是问题所在。
因此函数参数是原始对象/引用的副本。它在堆栈上分配。堆栈很好,因为在函数的结束/返回时简单地立即丢弃分配的值。当我的函数从堆栈中获取pass-by-value arg并返回它时会发生什么。看,它在堆栈上。是否将该对象/引用的堆栈alloc复制并重新分配到堆上?
Java和C#究竟/精确地发生了什么?
答案 0 :(得分:7)
听起来你在问Java中这样的效果是什么:
public static void f(Object object) {
return object;
}
public static void g() {
Dog dog = new Dog("Spike");
System.println(f(dog));
}
如果是这样,答案是,当调用g时:
在堆上分配内存,并且在g中称为dog的堆栈分配变量用于引用此内存。狗的“价值”是对象的参考;它占用了一个记忆词。
该值的副本通过寄存器或堆栈传递给f。 f获取自己的堆栈帧,除非编译器优化掉。但是,让我们说它确实得到了一个堆栈框架。包含地址副本值的简单单词放在此堆栈帧中。实际上,它与在某种程度上传递一个普通的旧整数没什么不同,正如你正确地指出Java中的所有内容都是按值传递。
当f返回时,它传递object的值,它本身只是一个指向原始Dog对象的内存字,返回给它的调用者。这个简单的指针值通常通过寄存器传回。关键是,只传回一个单词。
答案 1 :(得分:0)
在C#中,按值返回引用。
在下面的示例中,进入的内容与返回的内容相同。
Distance FindMinimum (Distance threshold)
{
Distance min = null;
foreach (Distance compare in AllDistance) {
if (compare > threshold && (min == null || compare < min))
min = compare;
}
if (min == null)
return threshold;
return min;
}
在下面的示例中,将返回对新找到的Distance对象的引用。
Distance FindNewThreshold (Distance threshold)
{
foreach (Distance compare in AllDistance) {
if (compare < threshold)
threshold = compare;
}
return threshold;
}
在上述两种情况下,传入的原始对象都不会更改。但在下面的示例中,将替换原始对象。
void FindNewThreshold (Distance threshold, ref Distance output)
{
foreach (Distance compare in AllDistance) {
if (compare < threshold)
threshold = compare;
}
output = threshold;
}
void Test ()
{
Distance d = new Distance (50);
Distance o;
AllDistance.Add(new Distance(10));
FindNewThreshold (d, ref o);
Console.WriteLine ("{0} {1}", d, o);
}
这将产生“50 10”。对o进行更改将影响AllDistance中的第一个对象。
答案 2 :(得分:0)
考虑'int'的情况。
public int returnIt(int arg) { return arg;}
并调用函数
int in = 6;
int out = returnIt(in);
调用该函数时,'in'的内容被复制到堆栈中。
当函数执行'return arg'时,内容被复制到(好吧,我不知道JVM中的哪个位置,在某些架构中它是指向堆栈中当前顶部的寄存器)。 / p>
然后从堆栈中回收'arg',但它的值已被复制。
当分配发生时,它不会从'arg'复制它从返回值的位置复制。
(当然,这可能都是在“现实生活中”进行了优化,这个例子很简单)
这就是你问的问题吗?
答案 3 :(得分:-1)
在C#上,只在堆栈上创建了结构,并且无法在堆栈上创建对象。
创建新结构和对象时存在差异。
使用 new 关键字创建新对象时,无论如何都始终在堆中创建对象,这是在C#中创建对象的唯一方法。垃圾收集器不会释放堆上对象的内存,直到没有其它对它的引用为止;这是直到对象的所有引用超出范围。
使用 new 关键字创建新结构时,无论如何都始终在堆栈中创建结构。将结构分配给另一个结构时,会发生成员方式的副本,而不是像对象一样发生的引用副本。
当一个对象按值传递给一个方法时,你在方法中收到的是对象引用的值(指向它的指针:所有C#对象都存储为指向它们在内存中的位置的指针)。当一个struct通过值传递给一个方法时,你收到的是它的成员副本。
注意:在将对象传递给方法时使用 ref 关键字时,意味着该方法可以更改引用指向的内存位置。
最后,您无法在方法内的堆栈中创建对象,并且通过返回传递给您的方法的对象,您将返回收到的相同引用。当您返回传递的结构时,将返回成员方式的副本。
在java上,概念是相似的,除了没有结构,也没有 ref 和 out 参数。