XNA Game-Themed CS1 Examples ( XGC1 ) Release 2.0 (XNA V3.1) 2/8/2010
Topic: Topic.3.ModulesAndFunctions
Example: Ex_9.ByValByRef

Functions: Passing Arguments By Value, and By Reference

References:

• Pre-requisite: it is assumed that you have read through the prior tutorials, and are familiar with the concepts covered in those tutorials.
• For this tutorial, it will be helpful to review the tutorial on translating Math Formulas into C#

Goals:

• In this tutorial, we will:
• Examine the difference between passing data to function by value, and by reference.

1. Obtain the example code

Download and unzip the zip file and you will see an ExampleProgram folder. Open the ExampleProgram folder, the EXE folder contains the compiled program and you can double click on the .sln file to work with the source code.

When the game starts, you'll see a screen that looks similar to this:

In addition to the standard "exit when you push the Back button" functionality, you can adjust the size of the right soccer ball by pushing the right thumbstick left or right, and you can move both soccer balls up or down by pushing the right thumbstick up or down..  Notice that this "game" is extremely simple - we have written it purely to demonstrate the difference between passing an argument to a function by reference, and passing an argument by value.

2. Examining The Program:

Let's examine the C# source code that produces the behavior we see on-screen

• Declaring the instance variables and named constants  public class Game1 : XNACS1Base {         private XNACS1Circle m_RefSoccer; // soccer ball radius changed by passing as reference     private XNACS1Circle m_ValSoccer; // soccer ball radius changed by passing as value (default)     #region Constants for circle/ball definition     private const float BALL_INIT_VALUE_X = 35.0f;     private const float BALL_INIT_REF_X = 65.0f;     private const float BALL_INIT_Y = 10.0f;     private const float BALL_INIT_RADIUS = 2.5f;     #endregion
• We'll need to keep track of each of the two circles  that we see on-screen, so we declare each of them here as instance variables.
• We also have several pre-set values that are defined using named constants.  We'll define the (X,Y) coordinates for the center of the "value" soccer ball ( which will be ( BALL_INIT_VALUE_X , BALL_INIT_Y ), as well as the (X,Y) coordinates for the center of the "reference" soccer ball ( which will be ( BALL_INIT_REF_X , BALL_INIT_Y ).  The starting radius for both soccer balls will be set to BALL_INIT_RADIUS .
• InitializeWorld():

We told C# to create instance variables for our Game1.  It's important that we give our variables well-defined values before we use them, like so:

 protected override void   InitializeWorld() {     World.SetWorldCoordinate( new Vector2(0,0), 100.0f);     m_RefSoccer = InitializeSoccer(BALL_INIT_REF_X);     m_ValSoccer = InitializeSoccer(BALL_INIT_VALUE_X); }
• Since this program is fairly simple we only need to initialize the two soccer balls.  Initially, they will be identical except for the X value of the coordinate of their center point, so the only argument that we need to pass to our new "InitializeSoccer" function is that X value.
• InitializeSoccer():  /// /// Allcoate memory and initialize soccer at different X positions /// /// The x-position /// An initialized Soccer with proper texture private XNACS1Circle InitializeSoccer( float xPos) {     // initialize the soccer ball     XNACS1Circle aSoccer = new XNACS1Circle ();     aSoccer.Center = new Vector2 (xPos, BALL_INIT_Y);     aSoccer.Radius = BALL_INIT_RADIUS;     aSoccer.Texture = "SoccerBall" ;     return aSoccer; }
• We've defined this function towards the end of the file, at the top of the "Local utility functions" region.
• This function is extremely similar to the InitializeRectangle function that was presented in the previous tutorial.
• Since we want to initialize the two circles using (nearly) identical steps, we have created  new function to do this work for us.  The function takes just one parameter - the X dimension of the circle's location ( xPos ).
• The function then creates a new XNACS1Circle object, and stores a reference to that object in the aSoccer local variable
XNACS1Circle aSoccer = new XNACS1Circle ();
• The next step is to set the on-screen location of the circle, by assigning the point (xPos, INIT_REC_Y) to the center of the circle:
aSoccer.Center = new Vector2 (xPos, BALL_INIT_Y);
• Next, we initialize the radius and texture (image) of the new circle object:
aSoccer.Texture =
"SoccerBall" ;
• Lastly, we return the reference to our newly created and initialized circle back to the calling method.

return aSoccer;

•  We start by creating two local variables, each of which holds a copy of one of the soccer ball's radii.  We do this because you can't pass an object's property by reference, so in order to clearly show the difference between passing by value, and passing by reference, we'll need to copy the each circle's radius into it's own local variable.
// Pay attention to the following function calls!!!

// 1. store radii of the two circles to local vars

• Next, we calculate the new radius of the circle using two nearly identical functions.  We'll examine the functions in more detail later, but for right now, the main thing to notice is that when passing an argument by reference, you need to put the word ref in front of the argument, as you can see on the second line:
// 2. Compute the new radius

// NOTICE: besides the "ref" keyword, the following two functions are identical!

• Another note about passing arguments by reference:  you can NOT pass literal numbers (such as 1, 3, or -14.25) by reference, only variables.  So the following would not compile:
• We then copy the values from the local variables back into the appropriate property on the appropriate circle:
// 3. assign the newly comptued radii to the respective circles

• Once we've done the above, we then call MoveSoccerByThumbstick, in order to move the soccer balls around.  While we've done this several times in the past, you should now be able to see that the circle objects are being passed by reference:
// 4. let user move the soccer balls up and down

//     You should think about byRef and byVal in this case!

MoveSoccerByThumbStick(m_ValSoccer);

MoveSoccerByThumbStick(m_RefSoccer);

• Finally, the program updates the messages at the top and bottom of the screen, so that it's very clear which variables were actually updated:
// 5. Output info to top & bottom of screen

// after calling the above, the two radii should be the same?!

EchoToTopStatus( "ByValSoccer_Y=" + m_ValSoccer.CenterY +

" ByRefSoccer_Y=" + m_RefSoccer.CenterY);

• Since these two functions are nearly identical, we will examine them both at the same time.  Furthermore, we will list them side by side, above, so it's clear how similar they are
• Both functions take a single parameter, which is the current radius, prior to any adjustments.
Both functions then assign that new value back to the radius parameter.
• HOWEVER, UpdateRadiusByRef has the ref modifier on it's parameter, which means that instead of creating a copy of the simple float argument, the radius parameter is actually a reference back to the argument.
• This means that when we call UpdateRadiusByRef, we're actually updating the argument byRefRadius , back in UpdateWorld.  So when we assign radius + GamePad.ThumbSticks.Right.X back to radius , we will change not only the radius variable here in UpdateRadiusByRef, but we will also be changing the byRefRadius variable back in UpdateWorld.
• In contrast, When we call UpdateRadiusByValue, then only the value of the argument is passed in, in the UpdateRadiusByValue method, the radius parameter is actually a copy of the value stored in the byValRadius variable, from the UpdateWorld method.
• This means that when we call UpdateRadiusByVal, we're NOT updating the argument byRefValue , back in UpdateWorld.  So when we assign radius + GamePad.ThumbSticks.Right.X back to radius , we will change only the radius variable here in UpdateRadiusByRef; the byRefValue variable back in UpdateWorld remains unchanged.
• It is because of this difference between normal, pass-by-value parameters, and pass-by-reference parameters that the soccer ball on the right will have it's radius changed back in the UpdateWorld method, while the soccer ball on the left will never change it's radius.
• MoveSoccerByThumbstick():  /// /// Change the centerY of aSoccer ball by right thumb stick y-movements. /// /// The soccer to be operated on private void MoveSoccerByThumbStick( XNACS1Circle aSoccer) {     // Change Ball position with Right Thumb Stick     aSoccer.CenterY += GamePad.ThumbSticks.Right.Y; }
• You've already seen functions that are nearly identical to this, so instead of focusing on the operation of the function, we want to focus on the fact that the parameter is being passed by reference, even though it's not declared using the ref modifer .
• You'll notice that we pass each of the two soccer balls in as arguments from the UpdateWorld method.  Here are the lines in UpdateWorld that pass in the two soccer ball objects:

// 4. let user move the soccer balls up and down

//     You should think about byRef and byVal in this case!

MoveSoccerByThumbStick(m_ValSoccer);

MoveSoccerByThumbStick(m_RefSoccer);

• You'll notice that after the two calls to MoveSoccerByThumbStick finish, that both soccer balls are updated.  This is because in C#, all objects are ALWAYS passed by reference, even without the ref modifier.

FURTHER EXERCISES:

1. Start from a blank starter project (1000.201, if you need it), and re-do the code from memory as much as possible.  On your first try, do what you can, and keep the above code open so that when you get stuck, you can quickly look up what you forgot (and that after you finish a line, so that you can compare your line to the 'correct' line).  On the next try, do the same thing, but try to use the finished code less.  Repeat this until you can type everything, without refering the tutorial's code.
• Repeat this exercise daily for several days, so that you really get the hang of this.  As you go on, periodically review this by re-doing this exercise.
2. Adding Parameters To A Function
For this exercise, you should use the same project that was explained in the above tutorial.

1. What happens when you only remove the ref keyword from the call to the UpdateRadiusByRef function, in UpdateWorld (but keep the ref keyword in the function definition)?
2. What happens when you only remove the ref keyword from the function definition (but keep the ref keyword in the call to the UpdateRadiusByRef function, in UpdateWorld )?
3. What are some advantages of being forced, by the C# language, to include the ref keyword in both the definition, and at all locations where the function is called?
4. Going back to the UpdateWorld function, and instead of creating new local variables, instead try to pass m_ValSoccer.Radius directly to the UpdateRadiusByVal method, and then try to pass m_RefSoccer.Radius directly to the UpdateRadiusByRef method