There are four different kinds of parameters in C#: value parameters
(the default), reference parameters (which use the ref
modifier), output parameters (which use the out
modifier),
and parameter arrays (which use the params
modifier).
You can use any of them with both value and reference types.
When you hear the words "reference" or "value" used (or use them
yourself) you should be very clear in your own mind whether you mean that
a parameter is a reference or value parameter, or whether you mean that the
type involved is a reference or value type. If you can keep the two ideas
separated, they're very simple.
By default, parameters are value parameters. This means that a new storage location is created for the variable in the function member declaration, and it starts off with the value that you specify in the function member invocation. If you change that value, that doesn't alter any variables involved in the invocation. For instance, if we have:
void Foo (StringBuilder x)
{
x = null;
}
...
StringBuilder y = new StringBuilder();
y.Append ("hello");
Foo (y);
Console.WriteLine (y==null);
Output:
The value of y
isn't changed just because x
is set to
null
. Remember though that the value of a reference type variable is
the reference - if two reference type variables refer to the same object, then
changes to the data in that object will be seen via both variables.
For example:
void Foo (StringBuilder x)
{
x.Append (" world");
}
...
StringBuilder y = new StringBuilder();
y.Append ("hello");
Foo (y);
Console.WriteLine (y);
Output:
After calling Foo
, the StringBuilder object that y
refers to contains "hello world", as in Foo
the data " world" was appended
to that object via the reference held in x
.
Now consider what happens when value types are passed by value. As I said before,
the value of a value type variable is the data itself. Using the previous definition
of the struct IntHolder
, let's write some code similar to the above:
void Foo (IntHolder x)
{
x.i=10;
}
...
IntHolder y = new IntHolder();
y.i=5;
Foo (y);
Console.WriteLine (y.i);
Output:
When Foo
is called, x
starts off as a struct with value
i=5
. Its i
value is then changed to 10.
Foo
knows nothing about the variable y
, and
after the method completes, the value in y
will be exactly the same
as it was before (i.e. 5).
As we did earlier, check that you understand what would happen if IntHolder
was declared as a class instead of a struct. You should understand why y.i
would be 10 after calling Foo
in that case.
Comments