John
John

Reputation: 1522

C# passing parameters by reference

I have the below piece of code which Prefixs a string to the start of each member of a string array. ie. ["a","b","c"] prefixed with "z" becomes ["za","zb","zc"].

private string[] Prefix(string[] a, string b) {
   for(int i = 0;i < a.Length;i++) {
     a[i] = b + a[i];
   }
  return a;
}

The function works fine (although if theres a better way to do this, I'm happy to hear it), but I'm having issues when passing parameters.

string[] s1 = new string[] {"a","b"};
string[] s2 = Prefix(s1,"z");

Now as far as I can tell, I'm passing s1 by Value. But when the Prefix function has finished, s2 and s1 have the same value of ["za,"zb"], or s1 has been passed by reference. I was certain you had to explicitly declare this behaviour in c#, and am very confused.

Upvotes: 2

Views: 563

Answers (9)

Joel Coehoorn
Joel Coehoorn

Reputation: 415600

As others have said, the reference is passed by value. That means your s1 reference is copied to a, but they both still refer to the same object in memory. What I would to do fix your code is write it like this:

private IEnumerable<string> Prefix(IEnumerable<string> a, string b) {
   return a.Select(s => b + s);
}

.

string[] s1 = new string[] {"a","b"};
string[] s2 = Prefix(s1,"z").ToArray();

This not only fixes your problem, but also allows you to work with Lists and other string collections in addition to simple arrays.

Upvotes: 5

user166390
user166390

Reputation:

The value of an object is passed. For "reference" objects this is the value of the reference. A clone/copy/duplicate of the underlying data is not made.

To fix the issue observed, simply don't mutate the input array -- instead, create a new output array/object and fill it in appropriately. (If you must use arrays, I would likely use this approach as it's so boringly C-like anyway.)

Alternately, you can clone the input array first (which also creates a new object). Using a clone (which is a shallow copy) in this case is okay because the inner members (strings) are themselves immutable -- even though they are reference types the value of the underlying object can't be changed once created. For nested mutable types, more care may need to be taken.

Here are two methods which can be used to create a shallow copy:

string[] B = (string[])A.Clone();

string[] B = (new List<string>(A)).ToArray();

It's not inclusive.

Personally though, I would use LINQ. Here is a teaser:

return a.Select(x => b + x).ToArray();

Upvotes: 1

Ani
Ani

Reputation: 113402

A reference to the string array is passed by value. Consequently, the original array reference in the calling method cannot be changed, meaning that a = new string[10] within the Prefix method would have no impact on the s1 reference in the calling method. But the array itself is mutable, and a duplicate reference to the same array is capable of making changes to it in a way that would be visible to any other reference to the same array.

Upvotes: 1

Gabe
Gabe

Reputation: 86698

Here's a better way to do it:

private string[] Prefix(string[] a, string b) {
  return a.Select(s => b + s).ToArray();
}

or even:

private IEnumerable<string> Prefix(IEnumerable<string> a, string b) {
  return a.Select(s => b + s);
}

Upvotes: 2

plinth
plinth

Reputation: 49179

Passing by value doesn't mean that you can't change things. Objects are passed by value, but the value is (effectively) a pointer to the object. Arrays are objects and a pointer to the array gets passed in. If you change the contents of the array in a method, the array will reflect the changes. This doesn't happen with strings only because strings are immutable - once constructed, their contents can't change.

Upvotes: 1

BoltClock
BoltClock

Reputation: 723388

Actually, a reference to s1 was passed by value to Prefix(). While you now have two different references, both s1 and s2 still refer to the same string array, as arrays are reference types in C#.

Upvotes: 1

John Nicholas
John Nicholas

Reputation: 4836

strings are immutable

this means that when you append a string to a string you get out a totally new string - its for performance reasons. its cheaper to make a new string that to reallocate the existing array.

hence why it feels like you are working with strings by value

in c# all reference types are passed by reference by default - ie classes creaped on the heap rather than values.

Upvotes: 1

driis
driis

Reputation: 164281

You are passing the reference to s1 by value. In other words, your a parameter (when in the Prefix function scope), and your s1 variable, are references to the same array.

Upvotes: 1

Powerlord
Powerlord

Reputation: 88786

C#, like Java before it, passes everything by value by default.

However, for reference types, that value is a reference. Note that both string and array are reference types.

Upvotes: 3

Related Questions