I am an experienced C and C++ programmer (managed and
traditional). I understand references
are being passed in a method parameter list, but I wouild like to know a little
more about pass-by-reference vs pass-by-value when objects are being passed.
consider:
public Class MyClassOne {
public
int myintone;
public
MyClassOne (MyClassTwo MC2) // forward reference
{
// do something to change MyClassTwo}
public
void MyFunctionOne (int i, MyClassTwo MCT) {
// do we need 'ref'
before MyClassTwo MCT?
MyClassTwo
LocalMyClassTwo; //*&* compiler error?do you need
a new()here?
LocalMyClassTwo =
MCT; //! note
// do stuff to
change MCT
}
public Class MyClassTwo {
//stuff here, including 'normal constructor' (C++ term)
}
static void
{
MyClassTwo MC2 =
new MyClassTwo();
MyClassOne MC1 =
new MyClassOne (MC2);
MC1.MyFunctionOne(10,MC2)
// OR, DO WE NEED
THIS VERSION?
// MC1.MyFunctionOne(10,ref MC2);
}
From the above, without belabouring the point, I think you
can see my concern: aside from the
'compile error' at *&* above, (I'm not sure it will compile, but that's
minor), my real question is whether "MyFunctionOne" is working on a
copy of the reference to MC2 (I assume not, otherwise the issue is complicated
by whether a shallow copy or a deep copy is being made), and, regardless,
whether MC2 will be changed by MyFunctionOne or not. If MC2 is being changed, why don't we (or do
we?) need the 'ref' keyword before it?
Thank you.
Fran
SamPosted Aug 2, 2007, 6:19 AM
Hi Fran,
Times where passing a reference type by reference is useful are limited
I can only think of 2 reasonably common situations where is may be neccessary:
1. When dealing with strings, which behave like value types and need to be passed by ref if you want to change them.
2. If you want a method to create and return more than one object (using out parameters).
Sam.
AlanPosted Aug 2, 2007, 6:01 AM
Yes, any 'orphaned' objects will be eligible for garbage collection and will be cleaned up by the GC in due course.
Passing reference types by reference is rarely needed but the C# language designers must have decided to allow it for completeness.
When I said in an earlier post that a managed pointer would be created which points to the address of the variable, I did in fact mean 'variable' rather than 'object' because, in the case of a reference type, the variable would contain a reference to where the object was stored on the heap rather than the object itself. So, in effect, you've got a reference to a reference here.
IIRC, this is the only occassion where managed pointers (i.e. pointers tracked by the GC) are used by C# (albeit implicitly) though they can be used explicitly in both Managed C++ and MSIL.
Francois AppertPosted Aug 1, 2007, 4:16 PM
Hello Sam:
Thank you. I think I understand this topic now, based on the Microsoft example below, part of which I reproduce below (the best parts). I now understand what you mean by saying 'passing Reference types by reference is rarely done' (or needs to be done), since, if you look at Example 5 below (and compare it to Example 4), you'll see that if you pass by reference rather than by value, and you change the reference, you can easily "orphan" your original data structure "pointed to" by the (now changed) reference. I suppose the C# garbage collector will clean up the "orphaned" data, and it won't be a memory leak (please confirm this if you care too), but it seems that indeed doing something like Example 5 below is "rare" or "rarely needs to be done" in an 'ordinary' program (please confirm). Fran (I see you fixed the server! Great. PS--if this site takes donations I'd like to donate--it's been very helpful in the past just reading it--this is my first post)
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vclrfPassingMethodParameters.asp
Example 4: Passing Reference Types by Value
The following example demonstrates passing a reference-type parameter, myArray, by value, to a method, Change. Because the parameter is a reference to myArray, it is possible to change the values of the array elements. However, the attempt to reassign the parameter to a different memory location only works inside the method and does not affect the original variable, myArray.
// PassingParams4.cs
// Passing an array to a method without the ref keyword.
// Compare the results to those of Example 5.
using System;
class PassingRefByVal
{
static void Change(int[] arr)
{
arr[0]=888; // This change affects the original element.
arr = new int[5] {-3, -1, -2, -3, -4}; // This change is local.
Console.WriteLine("Inside the method, the first element is: {0}", arr[0]);
}
public static voidMain ()
{
int[] myArray = {1,4,5};
Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", myArray [0]);
Change(myArray);
Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", myArray [0]);
}
}
Output
InsideMain , before calling the method, the first element is: 1
Inside the method, the first element is: -3
InsideMain , after calling the method, the first element is: 888
Code Discussion
In the preceding example, the array, myArray, which is a reference type, is passed to the method without the ref parameter. In such a case, a copy of the reference, which points to myArray, is passed to the method. The output shows that it is possible for the method to change the contents of an array element (from 1 to 888). However, allocating a new portion of memory by using the new operator inside the Change method makes the variable arr reference a new array. Thus, any changes after that will not affect the original array, myArray, which is created insideMain . In fact, two arrays are created in this example, one inside Main and one inside the Change method.
Example 5: Passing Reference Types by Reference
This example is the same as Example 4, except for using the ref keyword in the method header and call. Any changes that take place in the method will affect the original variables in the calling program.
// PassingParams5.cs
// Passing an array to a method with the ref keyword.
// Compare the results to those of Example 4.
using System;
class PassingRefByRef
{
static void Change(ref int[] arr)
{
// Both of the following changes will affect the original variables:
arr[0]=888;
arr = new int[5] {-3, -1, -2, -3, -4};
Console.WriteLine("Inside the method, the first element is: {0}", arr[0]);
}
public static voidMain ()
{
int[] myArray = {1,4,5};
Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", myArray [0]);
Change(ref myArray);
Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", myArray [0]);
}
}
Output
InsideMain , before calling the method, the first element is: 1
Inside the method, the first element is: -3
InsideMain , after calling the method, the first element is: -3
Code Discussion
All of the changes that take place inside the method affect the original array inMain . In fact, the original array is reallocated using the new operator. Thus, after calling the Change method, any reference to myArray points to the five-element array, which is created in the Change method.
Francois AppertPosted Aug 1, 2007, 4:15 PM
Hello Sam:
Thank you. I think I understand this topic now, based on the Microsoft example below, part of which I reproduce below (the best parts). I now understand what you mean by saying 'passing Reference types by reference is rarely done' (or needs to be done), since, if you look at Example 5 below (and compare it to Example 4), you'll see that if you pass by reference rather than by value, and you change the reference, you can easily "orphan" your original data structure "pointed to" by the (now changed) reference. I suppose the C# garbage collector will clean up the "orphaned" data, and it won't be a memory leak (please confirm this if you care too), but it seems that indeed doing something like Example 5 below is "rare" or "rarely needs to be done" in an 'ordinary' program (please confirm). Fran (I see you fixed the server! Great. PS--if this site takes donations I'd like to donate--it's been very helpful in the past just reading it--this is my first post)
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vclrfPassingMethodParameters.asp
Example 4: Passing Reference Types by Value
The following example demonstrates passing a reference-type parameter, myArray, by value, to a method, Change. Because the parameter is a reference to myArray, it is possible to change the values of the array elements. However, the attempt to reassign the parameter to a different memory location only works inside the method and does not affect the original variable, myArray.
// PassingParams4.cs
// Passing an array to a method without the ref keyword.
// Compare the results to those of Example 5.
using System;
class PassingRefByVal
{
static void Change(int[] arr)
{
arr[0]=888; // This change affects the original element.
arr = new int[5] {-3, -1, -2, -3, -4}; // This change is local.
Console.WriteLine("Inside the method, the first element is: {0}", arr[0]);
}
public static voidMain ()
{
int[] myArray = {1,4,5};
Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", myArray [0]);
Change(myArray);
Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", myArray [0]);
}
}
Output
InsideMain , before calling the method, the first element is: 1
Inside the method, the first element is: -3
InsideMain , after calling the method, the first element is: 888
Code Discussion
In the preceding example, the array, myArray, which is a reference type, is passed to the method without the ref parameter. In such a case, a copy of the reference, which points to myArray, is passed to the method. The output shows that it is possible for the method to change the contents of an array element (from 1 to 888). However, allocating a new portion of memory by using the new operator inside the Change method makes the variable arr reference a new array. Thus, any changes after that will not affect the original array, myArray, which is created insideMain . In fact, two arrays are created in this example, one inside Main and one inside the Change method.
Example 5: Passing Reference Types by Reference
This example is the same as Example 4, except for using the ref keyword in the method header and call. Any changes that take place in the method will affect the original variables in the calling program.
// PassingParams5.cs
// Passing an array to a method with the ref keyword.
// Compare the results to those of Example 4.
using System;
class PassingRefByRef
{
static void Change(ref int[] arr)
{
// Both of the following changes will affect the original variables:
arr[0]=888;
arr = new int[5] {-3, -1, -2, -3, -4};
Console.WriteLine("Inside the method, the first element is: {0}", arr[0]);
}
public static voidMain ()
{
int[] myArray = {1,4,5};
Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", myArray [0]);
Change(ref myArray);
Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", myArray [0]);
}
}
Output
InsideMain , before calling the method, the first element is: 1
Inside the method, the first element is: -3
InsideMain , after calling the method, the first element is: -3
Code Discussion
All of the changes that take place inside the method affect the original array inMain . In fact, the original array is reallocated using the new operator. Thus, after calling the Change method, any reference to myArray points to the five-element array, which is created in the Change method.
SamPosted Aug 1, 2007, 3:25 PM
Hi
Had a look at your workaround in the other thread
My point was that if you pass a reference type variable by value you can't
change the object to which is refers to inside that method.
Your workaround doesn't actually change the object referenced by a variable
It meerly returns a deep copy of the parameter which is then, outside of the method,
asigned to the same variable as is passed as a parameter.
That said, as me and Alan both said earlier, there are relatively few situations where
you will actually want to do such a thing, so reference types are very rarely passed
by reference!
Sam.
Francois AppertPosted Aug 1, 2007, 2:50 PM
SamPosted Aug 1, 2007, 10:35 AM
Here it is:
using
System;using
System.Collections.Generic;using
System.Text;namespace
ConsoleApplication1{
class Program{
static void Main(string[] args){
MyObject A = new MyObject();A.AMember = 1;
Methods.PassObjectByValue1(A); Console.WriteLine(A.AMember);// 2 MyObject B = new MyObject();B.AMember = 1;
Methods.PassObjectByValue2(B); Console.WriteLine(B.AMember);// 1 MyObject C = new MyObject();C.AMember = 1;
Methods.PassObjectByRef1(ref C); Console.WriteLine(C.AMember);// 2 MyObject D = new MyObject();D.AMember = 1;
Methods.PassObjectByRef2(ref D); Console.WriteLine(D.AMember);// 2 Console.ReadKey();}
}
class MyObject{
public int AMember;}
class Methods{
public static void PassObjectByValue1(MyObject X){
// I can change the values of member of an // parameter of a reference type passed by valueX.AMember = 2;
}
public static void PassObjectByValue2(MyObject X){
//I can't change the objectX =
new MyObject();X.AMember = 2;
}
public static void PassObjectByRef1(ref MyObject X){
// I can change the values of member of an // parameter of a reference type passed by refX.AMember = 2;
}
public static void PassObjectByRef2(ref MyObject X){
//I can also change the object that is referencedX =
new MyObject();X.AMember = 2;
}
}
}
Hope clear things up.
Sam
SamPosted Aug 1, 2007, 10:20 AM
I'll post an example in a sec
Sam
Francois AppertPosted Aug 1, 2007, 10:00 AM
AlanPosted Aug 1, 2007, 8:56 AM
Yes the object referenced by MCT will be permanently changed and, since this is the same object as is referenced by MC2, the changed object will still be accessible when the method ends.
Sam has already explained why passing reference types by reference is occasionally useful (because you can change the actual object being referenced) though, in my experience, this is not done very often.
You're right, Fran, that most books on C# seldom contain examples of this sort of thing (though one I have by Herb Schildt does) and this may be partly because it's not considered particularly important and partly because it's thought difficult for beginners to grasp.
SamPosted Aug 1, 2007, 8:13 AM
if you pass a reference type to a method by reference (using the ref keyword)
you have a reference to a reference to an object
The are few times when this gives any benifit, however it does give you the
option of not only editing (perminantly) the members of the object
but also making the reference refer to a different object altogether.
For a better explaination of this try Alan's 1st post
Sam
Francois AppertPosted Aug 1, 2007, 7:35 AM
AlanPosted Aug 1, 2007, 6:19 AM
As I'm having trouble reading your post because of a problem we occasionally have on here when formatting text, perhaps it would help if I just explained the basic position with parameters which are reference types.
1) If you pass the parameter by value (i.e. without 'ref' or 'out') then a copy of the reference is made and assigned to the parameter. This means that the method can change the state of the object to which the parameter refers and this will be persisted after the method ends.
2) If you pass the parameter by reference (i.e with 'ref' or 'out') then a managed pointer which points to the address of the variable is created and assigned to the parameter. This means that not only can the method change the state of the object to which the parameter initially refers but it can even change the object to which that parameter refers. Again these changes are persisted when the method ends.
EDIT:
Since making this post the format problem has now miraculously cleared and I can now read your post.
Yes, a copy of the reference is being made but this means that both MCT and MC2 refer to the same MyClassTwo object and so any changes which you make to this object (via the MCT reference) will be persisted after the method ends when accessing the object through the MC2 reference.