Skip to main content
GameDev.net gamedev.net
🔒 Locked

Ch.11 Methods and passing reference

Started by foolios Jul 30, 2007 at 12:31 AM 9 replies 2.9k views
Original Post
foolios
foolios
Ok, We're really starting to get somewhere. Chapter 11 with the new week beginning. So, I guess it would be a good time for me to take a look at how I am looking at code and let people start straightening me out before I get too deep and off the beaten path of righteous understanding. I placed comments of what I think is going on at each step. Please correct me and/or add opinions on what I should be noticing.

//Use classes from the System Namespace
using System; 
//Define a class
class PassArrayByValue 
{ 
    //Create the starting point of not only this class but of the program itself
    static void Main() 
    {
        //Allocate an area of memory for an array of type int
        //At the same time, this allocated memory will be sized to hold 10 int      //+objects
        int[] arr = new int[10]; 
        //Assign a value of 22 to the index of Zero/first pos. of the array
        arr[0] = 22;
        //Now pass the reference of the array object to the AlterInteger Method
        //This is the address to its location in memory
        AlterInteger(arr);
        //Write out to the console the value at index 0/First index 
        Console.WriteLine(arr[0]);
        //Used this to pause the dos window so it wouldn't close on me
        //for reading purposes
        Console.Read();
    } 
    //A method that won't return a value
    //This method will only accept array objects of type int
    static void AlterInteger(int[] arr) 
    { 
        //Index 0/first position of the array is now being assigned a value of //55
        arr[0] = 55;
        //Since this is an object reference, the array object itself will actually be modified to this value.  
    }
}

Thanks in advance. [Edited by - JWalsh on July 30, 2007 9:44:18 AM]
I rate users. Its just not having much impact as my rating is low...
foolios
foolios
Quote:

Now, add the following statement to the bottom of AlterInteger: arr = null; The program still works as before. The method is only setting its copy of the arr reference to null, so Main still has its original arr reference and nothing bad happens.


The local reference to the array is the only one that is now pointing to a null area in memory? But not the original array reference in Main? So, it's a copy of the reference that now exists in the AlterInteger method? Like it would have been a copy of a value if it was value passing paramaters?
I got lost here.
Why does setting null only affect the local copy as opposed to how we've been able to affect the actual object in the heap just previously?

Thanks in advance.

[Edited by - foolios on July 30, 2007 1:38:15 AM]
I rate users. Its just not having much impact as my rating is low...
foolios
foolios
Below, the book then uses the ref keyword when passing the reference to the object as a parameter. I don't see the difference between this and what I've been posting above. The previous examples that the book is using, then followed by this example.

Quote:

using System;
class PassArrayByReference
{
static void Main()
{
int[] arr = new int[10];
arr[0] = 22;
AlterInteger(ref arr);
Console.WriteLine(arr[0]);
}
static void AlterInteger(ref int[] arr)
{
arr[0] = 55;
arr = new int[5];
}
}
Now the arr reference itself declared in Main is passed by reference, so that the method is able to alter the arr itself in Main rather than just elements of the array. The method sets arr to a new array, and each element of a nearly allocated array has an initialized value of 0. The program displays the number 0.


I guess it's the line:
so that the method is able to alter the arr itself in Main rather than just elements of the array.
that I just don't understand.
I rate users. Its just not having much impact as my rating is low...
SamLowry
SamLowry
First, it's very important to understand the difference between value types and reference types:

Value types
int a = 5;

This has the effect that somewhere in memory enough space will be allocated to hold an integer. The variable a represents this little parcel of memory. Reading from and writing to a is the same as reading from and writing from this piece of memory.

Reference types
int[] arr = new int[] { 1, 2, 3 };

Here, we have two separate entities:

  • The variable arr is just large enough to contain a reference (which is in fact a memory address, i.e. a glorified number).

  • The array itself, located in memory at the address contained by arr.


Hence, we have two separate pieces of memories: a small one which arr represents, and a larger one which arr contains a reference to.

So, if we write to the array (arr[0] = 0;), we first look at the address contained in arr, go there, and write 0 to the larger piece of memory. Writing to arr itself (arr = null;) consists of writing null to the small piece of memory.


You can see this as 2 kinds of chains: with value types, you have a chain with only one link. With reference types, it's a chain with two links where you hold one end, and the interesting stuff is located at the other end. We can generalize this by allowing any number of links. C++ allows this:
// C++ code!!!int* arr = new int[] { 1, 2, 3 };int** p = &arrint*** p2 = &pint**** p3 = &p2

This means that p3 contains a reference (not exact terminology, but let's not go there) to p2. p2 in turn refers to p, p refers to arr, and finally, arr refers to the array. So, given p3, you can reach the array, you just need to know how many times you need to follow the next link in the chain.
p3 -> p2 -> p -> arr -> the actual array


Argument passing: value types
A function always receives copies of the arguments passed (except when ref or out is used). In the case of value types, the chain looks like
x
hence only one link. This x gets copied when passed as an argument.
void Modify(int x){  ++x;}int a = 0;Modify(a);// a is still 0

Modify can't modify a because it only receives a copy.


Argument passing: reference types
What happens with reference types? The chain is
arr -> the actual array
and you pass arr as an argument. This means arr gets copied.
void Modify(int[] xs){  ++xs[0];}int arr = new int[] { 1, 2, 3 };Modify(arr);// arr == { 2, 2, 3 }

Given a copy of arr, Modify can still follow the chain links, and finds the original array, which it then can proceed to modify.

void Modify(int[] xs){  xs = null;}int arr = new int[] { 1, 2, 3 };Modify(arr);// arr == { 1, 2, 3 }

Again, Modify receives a copy of arr, and modifies this copy. Thus, this has no effect on the original arr.


ref/out on value types
So, if you want a function to be able to modify something, you need to consider its place in "the chain", and pass the link just before that something.

Now, let's say you have an int and you want a function to be able to modify it. C# makes this possible with ref and out. In this context, there's no difference between these two. (There are of course differences, which have been explained in other threads, but they don't matter here)
If you pass an argument with ref or out, the extra link is automatically made for you.
void Modify(ref int x){  ++x;}int a = 0;Modify(ref a);// a == 1

The chain in this case is

x -ref-> a

This extra link is hidden for you, but it is there. Modify receives a reference to a, follows this link, finds a and is able to modify it.


ref/out on reference types
If we apply this on reference types
void Modify(ref int[] xs){  ++xs[0];  xs = null;}int[] arr = new int[] { 1, 2, 3 };Modify(arr);// The original array is now { 2, 2, 3 }// but you can't reach it anymore, as arr is null

Chain:
xs -ref-> arr -> actual array
Modify receives the left link xs, and can modify everything located further down the chain. First, it modifies the actual array (first line), and then arr (line 2).


Summarized
Given a chain
a -> b -> c -> d -> e -> f
If you pass c to a function, it will receive a copy of c and have access to the original d, e and f.

Chain for value types without ref/out : argument
Chain for value types with ref/out : argument -ref-> var
Chain for reference types without ref/out : argument -> object
Chain for reference types with ref/out : argument -> var -> object


I sincerely hope my explanation has not caused any more confusion...

[Edited by - SamLowry on July 30, 2007 10:32:45 AM]
foolios
foolios
The line that I don't understand why it only modifies the copy:
xs = null;

But in every case of the method we can modify the index of the array. But for some reason setting the array to null changes between whether we have passed the object to the method by reference or not.
I don't understand why setting to null differs to setting index's.
We pass arr and we can adjust x[0], but not set x to null.
We pass arr by ref and we can adjust x[0] and set x to null.
This is what's crossing me. We set a value in both cases but we can't set to null and that's what I don't understand why it's so. I guess something else is happening when we are setting the array to null that I don't get why it's any different then the value assignment that has that access that null does not.

Thanks so much for showing me the differences between value and reference. I think I get them for the most part. It's this one lil' bit that I am still very curious about.
I rate users. Its just not having much impact as my rating is low...
DarkTimes
DarkTimes
Remember that 'xs' is just a reference to where the array is actually stored in memory, it is not the array itself. When you pass 'xs' into a method a copy is made of that reference, so when you set it to null you are only setting the copy of the reference to null.
Spoonbender
Spoonbender
Quote:
Original post by foolios
The line that I don't understand why it only modifies the copy:
xs = null;

What is xs?
It's a reference to an array. So what happens when you assign something (say, null) to the reference?
The reference will now "point to" null, instead of pointing to our array.
But we didn't change the actual array. We just changed one of the references that pointed to it.

What if we'd done this instead?
xs = new int[15]

Again, we're only assigning something to xs, which is a reference to an array. (because arrays are reference types)
So what happens is that the reference will get overwritten, pointing to the newly declared array, instead of the previous one.
But the old array hasn't been modified in either of these two cases. Only the reference pointing to it has been changed.

If you do something like
xs[3]
, then you get the integer stored at index 3 in whatever array xs is currently pointing to. So when you set this to a new value, the array is modified. It's still the same array, but some of its contents have been changed.

If you're coming from a C++ background, this might take a bit of getting used to (in C++, a reference always points to the same object, so in this respect, C# references have more in common with C++ pointers. They can be set to point to null, or to other objects, without modifying whatever object they previously pointed to.)

Quote:
We set a value in both cases but we can't set to null and that's what I don't understand why it's so. I guess something else is happening when we are setting the array to null that I don't get why it's any different then the value assignment that has that access that null does not.

It's not null that makes a difference. It's the assignment operator (=) behaving differently for value and reference types:
// First, let's create two arrays to play around with,// and store references to them in the variables arr20 and arr30int[] arr20 = new int[20];int[] arr30 = new int[30];// Now let's create another variable we can try overwriting.int[] arr = null; // Right now, arr contains a null reference.// So if we assign one of our existing arrays to arrarr = arr20; // arr now points to *the same* array as arr20// and the null reference previously stored in arr is overwritten.// And if we assign another array to the variable:arr = arr30; // arr's existing reference (to the 20-element array)// is overwritten with a reference to the 30-element array.// Still, all we have manipulated so far are the references stored in arr// The actual arrays they pointed to haven't been modified// And both arr20 and arr30 still point to their original, unchanged arrays.// And back to startarr = null; // If we assign null to arr, we once again only overwrite a// reference. The 30-element array is untouched. And arr30 still points to that.

Conclusion: Whenever you assign (using the = operator) reference types (such as arrays), the result is a simple copying of references. The objects pointed *to* by the references are unchanged.

Now let's try with value types:
arr = arr20; // First, make arr and arr20 point to the same 20-element array, // just so we have something to play with.arr[0] = 42; // Assign a value-type (an integer) to the 0'th place in the array.// What is actually stored at the 0'th place is the value itself,// and *not* a reference, because we're storing value-types.// So the assignment copies the actual value into the 0'th place of // the array referenced by arr.// Both arr and arr20 still point to the same array,// and this array has had its 0'th element modified.// This is the same as would happen here:int i = 42; // Once again, we're assigning a value type,// so the actual *value* is copied over. At i's address,// the value 42 is now stored. No references are involved.// And just for the record,arr[0] = null; // Gives you a big fat error. You can't assign null to value types.// Null is a reference, so it can only be assigned to reference types.arr[0] = arr20; // Gives you an error for the same reason.// arr and arr20 might be a reference type, but arr[0] is an int,// which is a value-type, and you can't assign references to value-types


The trick isn't whether you assign null or not, but whether you assign to value or reference types. (and of course in the case of int arrays, you further have to keep in mind that the array is a reference type, but the ints it contains are value types. Still, as long as you keep track of whether an assignment works on the array itself, or on one of its elements, it all makes sense still.
It does take a bit of practice to get it straight. Read over SamLowry's post a couple of times, and the pieces should fall into place.
this guide helped me a lot when I started out with the language. You might find it useful.

[Edited by - Spoonbender on July 30, 2007 7:42:55 PM]
foolios
foolios
Thanks for your efforts all.

I was definitely looking at it all wrong. arr = null most definitely doesn't change the object. All it's doing is changing what arr is referencing.

I think that's what I got hung up on, that I was treating those two lines as if they were doing the same thing when they were not.

I learned a lot about something I thought I would never understand. Thanks so much!

The only thing so far that I am still fuzzy about is the use of the params.

But I think I've come a long way thanks to you guys and this workshop. I know I have a whole heck of a lot left to learn. But this has certainly helped me get past the roadblocks that had prevented me from getting this far before.

KUDOS!
I rate users. Its just not having much impact as my rating is low...
DarkTimes
DarkTimes
Quote:
Original post by foolios
The only thing so far that I am still fuzzy about is the use of the params.


Params just lets you pass multiple parameters into a method without knowing exactly how many parameters you're going to need to pass.

class Program{    static void Main()    {        MyMethod("Alex", "James", "Amy", "Phill", "George");    }    static void MyMethod(params string[] names)    {        foreach (string name in names)        {            System.Console.WriteLine(name);        }    }}

As you can see the multiple parameters which are passed are accessed through an array within the method. It's pretty simple really.
foolios
foolios
Your explanation made it very easy for me to see it.

Thanks so much!
I rate users. Its just not having much impact as my rating is low...
DarkTimes
DarkTimes
You're welcome.

As an addendum, which I neglected to add to avoid confusion, if you want to use normal parameters in addition to using the params keyword, they you need to be careful to force them to be set before you ask for params...

    class Program    {        static void Main()        {            MyMethod(22, "Alex", "James", "Amy", "Phill", "George");        }        static void MyMethod(int someNumber, params string[] names)        {            System.Console.WriteLine(someNumber);            foreach (string name in names)            {                System.Console.WriteLine(name);            }        }    }

As you can see in this example we pass an int into the method as well, and we can pass as many other values as we chose. We have to be careful that we pass these other values before we start with the whole params thing, as if we don't then we will get some nasty errors.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.