OK, before you jump to your feet, you need to understand what pass-by-value compared to pass-by-ref is. You may not agree with this definition of pass-by-value but that is merely semantics because the real question is what transpires between stack alloc and heap alloc.
Pass-by-value: The object to be passed is copied and the copy of the object is submitted as argument to a function (ok, OO purists you like to call it a "method" - semantics!). Therefore, at the end/return of the function, the original object is not modified regardless of what had been done to the copy of the object.
So Java (and presumably C# too) is a pass-by-value language. Some people think they are pass-by-ref but in actual fact the args being passed are references. So the copy of references is being passed to the function. That is, the reference is passed-by-value arg, because the original reference is not changed at the end/return of the function.
Now that we have gotten this out of the way, and come to accept my def of pass-by-value, here is the question.
So a function argument is a copy of the original object/reference. It is allocated on the stack. Stack is good because an allocated value is simply and immediately discarded at the end/return of the function. What happens when my function takes the pass-by-value arg from the stack and returns it. See, it is on the stack. Is the stack alloc of that object/reference copied and real开发者_StackOverflow中文版loc onto the heap?
What exactly/precisely happens in Java and C#?
It sounds like you are asking what is the effect of something like this in Java:
public static void f(Object object) {
return object;
}
public static void g() {
Dog dog = new Dog("Spike");
System.println(f(dog));
}
If so, the answer is, when g is called:
Memory is allocated on the heap, and a stack-allocated variable in g called dog is made to reference this memory. The "value" of dog is a reference to the object; it takes up one word of memory.
A copy of this value is passed, through a register or on the stack to f. f gets its own stack frame, unless optimized away by the compiler. But let's say it does get a stack frame. A simple word, containing the value of copy of the address is placed in this stack frame. Really, it is no different than passing a plain old integer in a way, as you correctly pointed out that everything in Java is pass-by-value.
When f returns, it passes the value of object, itself just a word of memory pointing to the original Dog object back to its caller. This simple pointer value is usually passed back through a register. The point is, only a word is passed back.
In C#, the reference is returned, by value.
In the example below, the same thing that went in is the same thing that is returned.
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;
}
In the example below, the reference to the newly found Distance object is returned.
Distance FindNewThreshold (Distance threshold)
{
foreach (Distance compare in AllDistance) {
if (compare < threshold)
threshold = compare;
}
return threshold;
}
In both cases above, the original object passed in is not changed. But in the following example, the original object will be replaced.
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);
}
This will produce "50 10". Making changes to o will affect the first object in AllDistance.
Consider the 'int' case.
public int returnIt(int arg) { return arg;}
and a call to the function
int in = 6;
int out = returnIt(in);
When the function is called, the contents of 'in' are copied onto the stack.
When the function executes 'return arg', the contents are copied into (well, I don't know where in the JVM, in some architectures it's to a register in some it's to the current top of the stack).
Then 'arg' is reclaimed from the stack, but it's value has already been copied.
When the assignment happens, it's not copying from 'arg' it's copying from the location of the return value.
(Of course, this is probably all optimised away in 'real life' for an example this simple)
Is that what you were asking?
On C#, only structs are created on the stack, and it is impossible to create an object on the stack.
There is a difference when creating new structs and objects.
When you create a new object using the new keyword, the object is always created in the heap no matter what, and this is the only way to create an object in C#. The garbage collector doesn't frees the memory for an object on the heap until there exists no other references to it; this is until all the references to the object are out of scope.
When you create a new struct using the new keyword, the struct is always created in the stack no matter what. When you assign a struct to another, a member-wise copy takes place instead of a reference copy as it happens with objects.
When an object is passed into a method by value, what you receive in the method is the value of the reference to the object (a pointer to it: all C# objects are stored as pointers to their location in memory). When a struct is passed by value into a method, what you receive is a member-wise copy of it.
Note: when you use the ref keyword when passing an object to a method, it means that the method can change the memory location at which the reference is pointing to.
Finally, there is no way you could create an object in the stack inside a method, and by returning an object that was passed to your method, you'll be returning the same reference that was received. When you return a struct that was passed, a member-wise copy will be returned.
On java, the concepts are similar, except that there are no structs, nor ref and out parameters.
精彩评论