Passing reference type as method parameter

  • Thread starter Thread starter Maxim
  • Start date Start date
M

Maxim

Hi!

A have a string variable (which is a reference type).
Now I define my Method like that:

void MakeFullName(string sNamePrivate)
{
sNamePrivate+="Gates"
}

The calling code:
string sName="Bill";
MakeFullName(sName);

My variable sName wasn't changed (the same "Bill"). String is a
reference type and I would expect
that it will be passed by reference without defining the method
parameter as "ref". Only after defining it as "ref" I get the expected
result ("BillGates").


void MakeFullName1(ref string sNamePrivate)
{
sNamePrivate+="Gates"
}

The calling code:
string sName="Bill";
MakeFullName1(ref sName);

In the following article Jesse Liberty explicitly says, that reference
parameters are passed by reference:
http://www.ondotnet.com/pub/a/dotnet/2002/02/11/csharp_traps.html?page=2

"Value types are passed to methods by value (a copy is made) while
reference types are effectively passed by reference."

Does it mean, that unless I declare my paramter as "ref" my value
outside the mehtod is never changed?

Thanks for you help.

Maxim
 
Hi Maxim,
you chose an unfortunate example for your testing. A string is a
reference type object but it has value type semantics, meaning a string is
immutable. If you change a string i.e you try to append, or change any part
of the string, then what is really happening is that you make a copy of the
entire string.

Classes are usually reference types, so in the example below the Person
instance is automatically passed by reference without the ref keyword:

class Person
{
private string _name;

public Person(string name)
{
_name = name;
}

public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
}

public static void Main(string[] args)
{
Person p = new Person("mark");
ChangePersonName(p);

//this will print "new name"
Console.WriteLine(p.Name);
}

public static void ChangePersonName(Person p)
{
p.Name = "new name";
}


In your example I would change the MakeFullName function return a string as
the result.

The ref keyword is only required when you want to pass value types like
references, which in my opinion is usually not desirable as it makes code not
as readable. You do not need the ref keyword to pass normal reference types
by reference, this will happen automatically.

Also beware that structs are value types not reference types :-)

Hope that helps
Mark R Dawson
 
Hi Maxim,

I think it means that unless you explicitly declare otherwise (by using
"ref"), then assume that
you're passing all parameters by value.

Anyone please correct me if I am wrong.

Cheers!
 
if you add the "ref" keyword to a reference type, you in fact pass a
reference to a reference. kind of like a pointer to a pointer in C.
without the ref, when you added the last name, you reassined
sNamePrivate to a new string, instead of what it was. with it, you
reassigned the original variable.
 
Try calling with the ref token before sName like this: MakeFullName(ref
sName);

class objects are automatically passed by reference. (the heap)
strings, structures etc. are passed by value. (the stack)

:-)
 
Value types are passed by value i.e. int's doubles etc, reference types are
passed by reference and do not require the ref keyword to be passed by
reference this is the default behaviour.

The ref keyword is used to allow you to pass value types by reference or to
pass a reference to a reference type.
 
By default all parameters in C# are passed by value. This is a very
subtle
concept in that the _variable_ to a reference type is passed by value,
not the
object. A copy of the variable goes on the stack. When the method
returns,
the variable is cleared off the stack. You can touch the actual object
from
within the method using the copy of the variable and IF the object is
mutable,
you can change the state of the object and this change will persist when
the
method returns. Unfortunately, in your example, you used an immutable
object. The local copy of the variable is assigned to a new immutable
string
and then the copy is cleaned off the stack. To prove the subtle
difference
between _effective_ pass by reference and true pass by reference, you
only
need to write a swap routine. The swap routine does not work as you
would
expect if C# supported true pass by refererence by default. To write a
swap
routine in C# you need to use the ref keyword.

This topic has been a source of great confusion to C++ coders, so I will
elaborate. If you want to write a swap routine then you need to add
another
degree of indirection to the method call. You can do this by use the
keyword
ref. Here is some sample code that demonstrates the concept. The swap
routine only works if you pass a reference to a concrete Drawable object
by
reference.

using System;
namespace TestSwap
{
abstract class Drawable
{
abstract public void DrawMe();
}
class Circle : Drawable
{
public override void DrawMe()
{
System.Console.WriteLine("Circle");
}
}
class Square : Drawable
{
public override void DrawMe()
{
System.Console.WriteLine("Square");
}
}
/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
static public void SwapByValue(Drawable d1, Drawable d2)
{
Drawable temp= d1;
d1 = d2;
d2= temp;
}
static public void SwapByRef(ref Drawable d1, ref
Drawable d2)
{
Drawable temp= d1;
d1 = d2;
d2= temp;
}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
Drawable wasCircle= new Circle();
Drawable wasSquare= new Square();
wasCircle.DrawMe(); // outputs Circle
wasSquare.DrawMe(); // outputs Square
SwapByValue(wasCircle, wasSquare); // fails
wasCircle.DrawMe(); // outputs Circle
wasSquare.DrawMe(); // outputs Square
SwapByRef(ref wasCircle, ref wasSquare); //
succeeds
wasCircle.DrawMe(); // outputs Square
wasSquare.DrawMe(); // outputs Circle
System.Console.ReadLine();
}
}
}

Regards,
Jeff
 
So to be clear. If this was a _mutable_ string class that you had
authored and in
the body of the method you called sName.append("Gates"), on exit the
calling
code sName variable would refer to an object with the content
"BillGates". This is
due to the fact that both variables refer to the same single object. If
you
reassign the local copy of the variable sName to a new object, the
calling sName
variable would still point to the original object with content "Bill".
When the
method returned, the local object with the content "BillGates" would
most likely
be eligible for garbage collection.

Regards,
Jeff
 
Ok. Thank you for your all help.
Now I understand my problem: String is immutable sequence of characters
and cosequently me returned the new instance. Changing the string to
muttable StringBuilder also gives desired result:

public void MakeFullNameEx(StringBuilder sNamePrivate)
{
sNamePrivate.Append("Gates");
}

StringBuilder sNameEx = new StringBuilder("Bill");
tst.MakeFullNameEx(sNameEx);
Console.WriteLine(sNameEx);

Output: BillGates

Hi Maxim,
you chose an unfortunate example for your testing. A string is a
reference type object but it has value type semantics, meaning a string is
immutable. If you change a string i.e you try to append, or change any part
of the string, then what is really happening is that you make a copy of the
entire string.

Classes are usually reference types, so in the example below the Person
instance is automatically passed by reference without the ref keyword:

class Person
{
private string _name;

public Person(string name)
{
_name = name;
}

public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
}

public static void Main(string[] args)
{
Person p = new Person("mark");
ChangePersonName(p);

//this will print "new name"
Console.WriteLine(p.Name);
}

public static void ChangePersonName(Person p)
{
p.Name = "new name";
}


In your example I would change the MakeFullName function return a string as
the result.

The ref keyword is only required when you want to pass value types like
references, which in my opinion is usually not desirable as it makes code not
as readable. You do not need the ref keyword to pass normal reference types
by reference, this will happen automatically.

Also beware that structs are value types not reference types :-)

Hope that helps
Mark R Dawson

Maxim said:
Hi!

A have a string variable (which is a reference type).
Now I define my Method like that:

void MakeFullName(string sNamePrivate)
{
sNamePrivate+="Gates"
}

The calling code:
string sName="Bill";
MakeFullName(sName);

My variable sName wasn't changed (the same "Bill"). String is a
reference type and I would expect
that it will be passed by reference without defining the method
parameter as "ref". Only after defining it as "ref" I get the expected
result ("BillGates").


void MakeFullName1(ref string sNamePrivate)
{
sNamePrivate+="Gates"
}

The calling code:
string sName="Bill";
MakeFullName1(ref sName);

In the following article Jesse Liberty explicitly says, that reference
parameters are passed by reference:
http://www.ondotnet.com/pub/a/dotnet/2002/02/11/csharp_traps.html?page=2

"Value types are passed to methods by value (a copy is made) while
reference types are effectively passed by reference."

Does it mean, that unless I declare my paramter as "ref" my value
outside the mehtod is never changed?

Thanks for you help.

Maxim
 
Mark,

Reference types are passed by value. Passing a reference type by reference
is something quite different.

Can I refer you to Jon Skeet's excellent article referenced by another
poster:

http://www.yoda.arachsys.com/csharp/parameters.html

There's enough confusion on this topic without spreading further
misunderstanding. Jesse Liberty's article doesn't help by saying
"effectively passed by reference", which is what confused the OP.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top