1

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?

Liath
  • 9,913
  • 9
  • 51
  • 81
  • Possible duplicate of http://stackoverflow.com/questions/636932/in-c-why-is-string-a-reference-type-that-behaves-like-a-value-type – DJ Quimby Feb 22 '12 at 16:49

4 Answers4

19

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.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
7

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?

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Nice analogy. I will try to remember that one when I next have to explain the concept to one of my Juniors. – afrischke Feb 23 '12 at 21:34
5

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;
}
BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
0

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.

Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
  • That *is* changing the value of `originalValue` - the variable's value is a reference, and this changes it to be a different reference. However, that change isn't visible to callers, because the argument is passed by value. – Jon Skeet Feb 22 '12 at 16:50
  • @JonSkeet - Poor choice of words on my part. I was referring to the string value that the reference stored. Tried re-wording things to make that more clear. Will probably just delete since I've been blown out of the water. – Justin Niessner Feb 22 '12 at 16:55
  • Unfortunately wording this sort of thing accurately and readably gets extremely tricky :( I tend to use the word "object" when I'm talking about the object (which is what I think you mean by "the string value"). – Jon Skeet Feb 22 '12 at 16:58