"Infinity was satisfied with your C# level." Ritchie said to me later that
day, after I had spoken with Infinity.
"So... does this mean that I'll be chosen for the Rust project?!" I asked.
"You're almost there Teo. You'll also need to pass an exam. If you do, then
you are The Chosen One. You'd better go get some rest. The exam will take
place tomorrow, and I've already said 'goodbye' to you in my mind, so I want
you to pass it."
I nodded and went to the elevator. But instead of going to my floor, I
headed to Nonames place in the basement.
When I got there, the atmosphere was... strange. The lights were flickering,
Noname was very quiet and instructions on his multiple screens just repeated
constantly. What was happening?!
"Hi Noname, how are you?" I asked.
"Hi Teo. I can't find the mistake. There is a mistake, I know, but I can't
find it. I-I-I can't find a mistake. I was looking for a mistake but I-I-I
c-c-couldn't find it. No mistake, but there is one. Can't find a mistake..."
Noname trailed off.
"Okay, okay... I get it. You're looking for a mistake, but you've had no
luck in finding it. I'm sorry to see you in this state, how can I help you?"
I asked, concerned.
"I have this method, but it doesn't change parameters that I pass to it. I
don't understand why. Please, look for an article in the local network,
maybe you'll find something useful about passing parameters to methods."
Noname begged me.
"Sure Noname, I will do! I'll come back in a little while." I tried to speak
optimistically, as I was definitely the only optimist in this room. Come to
think of it, I was the only
human
in the room, and I'm not sure one can apply 'optimistic' to a machine.
Still, I knew I had to try to understand what was so tricky about passing
parameters to methods...
In next to no time, I had found an article on the local network about
passing parameters to methods. I began to read:
"Let's take a simple exercise of calling a method,
PrintAndChangeInteger
, and passing an
int
type variable as the parameter."
static void Main()
{
int exampleInt = 8;
PrintAndChangeInteger(exampleInt);
Console.WriteLine($"int after the method call: {exampleInt}");
}
static void PrintAndChangeInteger(int integerToPrint)
{
integerToPrint++;
Console.WriteLine($"int inside the method call: {integerToPrint}");
}
// Outputs:
// int inside the method call: 9
// int after the method call: 8
The article continued, "You probably expected the output to be":
int inside the method call: 9
int after the method call: 9
"The trick here, is in
how
parameters are passed from the
Main
method to the
PrintAndChangeInteger
method. In the above example, the parameter
exampleInt
was
copied
. The method
PrintAndChangeInteger
was therefore working with a copy of the
exampleInt
variable
. That's why after the method
PrintAndChangeInteger
returned, the value of
exampleInt
remained the same.
This type of parameter passing (when the callee
copies
the caller's parameters) is called
passing by value
.
In this case, if the callee modifies the parameter variable, the effect
is not
visible to the caller.
As you can imagine, there is another type of parameter passing;
passing by reference
.
When a parameter variable is passed by reference, the caller, and the
callee use and change the same variable. If the callee modifies this
variable, the effect
is
visible to the caller."
"Built-in types (except
string
and
object
) by default, are
passed by value
.
This means that almost all types that you know, like
int
,
double
,
char
, etc, are passed in this way.
If you, as a programmer, want to pass a built-in type by reference, you
should use the keywords
ref
or
out
.
You should put them before the variable name when you call a method and in
the method's parameters list. Here is an example":
static void Main()
{
int exampleInt = 8;
//---Place-ref-here----↓
PrintAndChangeInteger(ref exampleInt);
Console.WriteLine($"int after the method call: {exampleInt}");
}
//----------Place-ref-here---------↓
static void PrintAndChangeInteger(ref int integerToPrint)
{
integerToPrint++;
Console.WriteLine($"int inside the method call: {integerToPrint}");
}
// Outputs:
// int inside the method call: 9
// int after the method call: 9
"As you can see in the example, when we passed the parameters by reference,
the
PrintAndChangeInteger
method was working with the same variable as the
Main
method. That's why the value of
exampleInt
changed and the output changed too":
int inside the method call: 9
int after the method call: 9
"But, 'what about
out
?' you may ask. The
out
keyword is very similar to
ref
. There are two main differences":
static void Main()
{
int exampleInt; // Not initialized (no value assigned)
ChangeIntegerWithRef(ref exampleInt); // Wrong - exampleInt is not initialized
ChangeIntegerWithOut(out exampleInt); // Ok
}
static void ChangeIntegerWithOut(out int integerToChange)
{
integerToChange = 2;
}
static void ChangeIntegerWithRef(ref int integerToChange)
{
integerToChange = 2;
}
static void Main()
{
int exampleInt = 2;
PrintIntegerWithRef(ref exampleInt);
PrintIntegerWithOut(out exampleInt); // This is ok
}
// This method is ok
static void PrintIntegerWithRef(ref int integerToPrint)
{
Console.WriteLine(integerToPrint);
}
// Wrong!!! When using out - assign a value to integerToPrint
static void PrintIntegerWithOut(out int integerToPrint)
{
Console.WriteLine(integerToPrint);
}
// This method is ok. It changes the value of integerToPrint
static void PrintIntegerWithOutCompiles(out int integerToPrint)
{
integerToPrint = 23;
Console.WriteLine(integerToPrint);
}
The article continued, "And finally, out in the wild, when you are going to
write real programs that are important, please, remember these simple
rules":
static void Main()
{
int input;
// out/ref usage to get a result from the method
GetNumberFromUserWithOut(out input);
// Return value usage to get a result from the method
input = GetNumberFromUserWithReturnValue();
}
static void GetNumberFromUserWithOut(out int input)
{
input = int.Parse(Console.ReadLine());
}
static int GetNumberFromUserWithReturnValue()
{
return int.Parse(Console.ReadLine());
}
"All types that are passed by value, by default, are called
Value Types
. Types that are passed by reference by default, are called
Reference Types
.
You can also remember the difference, by remembering that
value types
hold the value inside itself, but
reference types
hold only the reference to a value.
Among the types you have learned,
string
and
array
are reference types.
The rest are value types.
Look at this comparison diagram for
int
and
string
variables":
The article finished with an example. "Here's an example, where we pass an
array
to the method, and as an
array
is a reference type, it is
not
copied; the method works with the same
array
as the caller and changes it":
static void Main()
{
int[] exampleArray = { 1, 2, 3 };
PrintAndChangeArray(exampleArray);
Console.WriteLine($"Array after the method call:");
for (int i = 0; i < exampleArray.Length; i++)
{
Console.Write($"{exampleArray[i]} ");
}
}
static void PrintAndChangeArray(int[] arrayToPrint)
{
Console.WriteLine($"Array inside the method call:");
arrayToPrint[0] = 12;
for (int i = 0; i < arrayToPrint.Length; i++)
{
Console.Write($"{arrayToPrint[i]} ");
}
Console.WriteLine();
}
// Outputs:
// Array inside the method call:
// 12 2 3
// Array after the method call:
// 12 2 3
I really liked the article. It was clear and easy to understand. After
reading everything, I ran to the basement to fix Noname's module. What I
found there wasn't the easiest task in the world... but I knew I'd fix it!