Liath
Liath

Reputation: 10191

String behaving like a Value Type

I have just written a function and I don't understand why I'm getting the result I am:

private void ReplaceIfEmpty(string originalValue, string newValue)
{
  if (string.IsNullOrWhitespace(originalValue))
  {
    originalValue= newValue;
  }
}

When I call this function the originalValue is not updated. My understanding is that a string is a class, therefore it's a reference type, therefore the value I pass in should be updated. Can you explain why it isn't?

Upvotes: 1

Views: 418

Answers (4)

Eric Lippert
Eric Lippert

Reputation: 659974

You passed a reference to a string. You did not pass a reference to a variable. If you want to change a variable then you do it like this:

private void ReplaceIfEmpty(ref string originalValue, string newValue)  ...

The difference is frequently confusing to people. Think about it this way. Instead of imagining a string, imagine two houses. Now imagine two pieces of paper that have the addresses of those houses; those are references to the houses. And now imagine four drawers that each contain a piece of paper. The drawers are labelled p, q, x and y:

void M(House x, House y)
{
    x = y;
}
...
House p = new House("123 Sesame Street");
House q = new House("1600 Pennsylvania Avenue");
M(p, q);

What does this program do? You put a piece of paper that says "123 Sesame Street" in drawer p. You put a piece of paper that says "1600 Pennsylvania Avenue" in drawer q.

You make a photocopy of the paper in drawer p and put the copy in drawer x. You make a photocopy of the paper in drawer q and put the copy in drawer y. And then you call M. M makes a photocopy of what's in drawer y and puts it in drawer x. Drawer p is not affected.

Now consider this case:

void M(ref House r, House s)
{
    r = s;
}
...
House p = new House("123 Sesame Street");
House q = new House("1600 Pennsylvania Avenue");
M(ref p, q);

What does this program do? You put a piece of paper that says "123 Sesame Street" in drawer p. You put a piece of paper that says "1600 Pennsylvania Avenue" in drawer q.

You put a sticky note on drawer p that says "this drawer is also called r".

You make a photocopy of the paper in drawer q and put the copy in drawer s.

And then you call M. M makes a photocopy of what's in drawer s and puts it in drawer r, which is the same as drawer p.

Make sense?

Upvotes: 7

BrokenGlass
BrokenGlass

Reputation: 160852

You are not changing the value of variable that you passed as the parameter originalValue, you are trying to assign a new instance to it, which for references means that the variable points to a new reference - to update a reference you need to pass the string by ref - by default references are passed by value so the variable that you pass never gets updated:

private void ReplaceIfEmpty(ref string originalValue, string newValue)
{
  if (string.IsNullOrWhitespace(originalValue))
  {
     originalValue = newValue;
  }
}

A much better approach would be to just return the new string instead:

private string ReplaceIfEmpty(string originalValue, string newValue)
{
   if (string.IsNullOrWhitespace(originalValue))
      return newValue;
   else
      return originalValue;
}

Or even more convenient make it an extension method:

public static string ReplaceIfEmpty(this string originalValue, 
                                    string replaceValue)
{
   if (string.IsNullOrWhitespace(originalValue))
      return replaceValue;
   else
      return originalValue;
}

Upvotes: 5

Jon Skeet
Jon Skeet

Reputation: 1499860

This has nothing to do with reference types vs value types really.

You're changing the value of a parameter:

originalValue= newValue;

For "normal" parameters which don't have the ref or out modifier, that change will never be visible.

See my article on parameter passing for more information, along with my article on reference types and value types to make sure you understand why sometimes it "looks" like reference types are passed by reference by default. (They're not: all arguments are passed by value by default, it's just that for reference types, the argument value is a reference, not an object, so changes made to the object are still visible from the caller.)

So you could make originalValue a ref parameter - but it would be better to make the method return a string instead. I'm generally reluctant to use ref parameters; code is usually easier to understand without them.

Upvotes: 19

Justin Niessner
Justin Niessner

Reputation: 245399

When you do the following assignemnt:

originalValue = newValue;

You're not changing the string value that was referenced by originalValue, you're changing the value of originalValue to point to the same location as newValue.

Strings are immutable and can't be modified after they're set, only re-assigned.

Upvotes: 0

Related Questions