0

Why does ArrayCalledWithOrderBy not change the original passed array but ArrayCalledWithSort does?

EDIT: As for the links that suggest duplicate. One link does not even has the OrderBy in it, so it is clearly not a duplicate. The other link is actually asking which is better option between OrderBy and Sort, and not why one is not changing the original array.

public static void ArrayCaller()
{
   var arr = new string[] { "pink", "blue", "red", "black", "aqua" };
   DisplayArray(arr, "Before method calls");

   ArrayCalledWithOrderBy(arr);
   DisplayArray(arr, "After method call using orderby");

   ArrayCalledWithSort(arr);
   DisplayArray(arr, "After method call using sort");
}

public static void ArrayCalledWithOrderBy(string[] arr)
{
    // this does not change the original array in the calling method. Why not?
    arr = (arr.OrderBy(i => i).ToArray()); 
    DisplayArray(arr, "After orderby inside method");
}

public static void ArrayCalledWithSort(string[] arr)
{
    // this changes the original array in the calling method. 
    // Because it is using the same reference? 
    // So the above code for orderby does not?
    Array.Sort(arr);
    DisplayArray(arr, "After sort inside method");
}

public static void DisplayArray(string[] arr, string msg)
{
    for (int i=0; i<arr.Length; i++)
    {
        Console.Write($"{arr[i]} ");
    }
    Console.WriteLine($" - {msg}");
} 

// OUTPUT
//pink blue red black aqua  - Before method calls
//aqua black blue pink red  - After orderby inside method
//pink blue red black aqua  - After method call using orderby
//aqua black blue pink red  - After sort inside method
//aqua black blue pink red  - After method call using sort
niki b
  • 989
  • 3
  • 10
  • 30
  • @NisargShah No. This question asks why the overwritten `arr` array in `ArrayCalledWithOrderBy` doesn't update the `arr` array in `ArrayCaller()`. [This question](https://stackoverflow.com/questions/10792603/how-are-strings-passed-in-net) is about the passing of strings, but could easily be altered for arrays. – Cole Tobin Aug 06 '17 at 06:45
  • It seems clear to me: *I am just confused why the OrderBy below does not change the original passed Array but the Sort does. Can someone please explain what is going on?* – Nisarg Shah Aug 06 '17 at 06:46
  • @ColeJohnson you shouldn't just change the entire question to make it *not a duplicate*. If you want clarifications from the OP, you might want to ask in the comments. – Nisarg Shah Aug 06 '17 at 06:49
  • 1
    @NisargShah. Looks like the link is trying to determine which is the better choice. I am trying to determine why one changes the original array but the other does not. – niki b Aug 06 '17 at 06:51
  • 1
    @NisargShah It wasn't a duplicate in the first place. It's pretty obvious the OP asked why changing `arr` doesn't get reflected in `ArrayCaller()` – Cole Tobin Aug 06 '17 at 06:51
  • @ColeJohnson Thanks for your answer below. And no, I also do not think it is a duplicate question. I will tag your answer below as accepted. It says I need to wait 7 more minutes to be able to do so. – niki b Aug 06 '17 at 06:53
  • @nikib The answer to the linked question highlights that `Sort` updates the original array, while `OrderBy` creates a separate copy and orders the elements within the new array. – Nisarg Shah Aug 06 '17 at 06:53
  • 1
    @ColeJohnson I don't mind Niki agrees with you. But from the way it was originally phrased, it seemed a clear duplicate. – Nisarg Shah Aug 06 '17 at 06:55
  • 1
    From your statement `arr = (arr.OrderBy(i => i).ToArray());`, I infer that you do understand that `OrderBy()` does not modify the array. If you don't, see second marked duplicate. It seems what you _don't_ understand is that method parameters, by default, are passed by-value and that changes to those parameter values won't affect the caller's variable (if any). You need to use `ref` or `out` to modify the caller's variable. See first marked duplicate for more detail on that, including how it applies in your exact situation (i.e. with arrays). – Peter Duniho Aug 06 '17 at 07:08
  • @PeterDuniho. Yes I am still trying to grasp the concepts. You said "method parameters, by default, are passed by-value and that changes to those parameter values won't affect the caller's variable" yet the Array.Sort(array) did. So that seems confusing. And it looks like it changed because the "value" that was passed was a reference. Hard to grasp for a newbie. But thanks for your input. I will look again the links above. – niki b Aug 06 '17 at 07:25
  • _"yet the Array.Sort(array) did"_ -- **no**, it did not. It modified the _object_ that the caller's variable `arr` refers to. But the variable `arr` remained unmodified. It still referred to the same array object. Conversely, in the `ref` scenario, the object the `arr` variable (in the caller and the called method) refers to _is_ changed, i.e. the variable itself is modified to refer to a different object. – Peter Duniho Aug 06 '17 at 07:55

3 Answers3

3

Arrays are reference types, so when you pass an array to a method. Its reference is passed by value.

You should be aware that the arr in this method:

public static void ArrayCalledWithOrderBy(string[] arr)
{
    arr = (arr.OrderBy(i => i).ToArray()); 
    DisplayArray(arr, "After orderby inside method");
}

and the arr here:

var arr = new string[] { "pink", "blue", "red", "black", "aqua" }; DisplayArray(arr, "Before method calls");

ArrayCalledWithOrderBy(arr);

Are two different variables holding references to the same array, until this line:

arr = (arr.OrderBy(i => i).ToArray()); 

In this line you change arr to refer to the sorted array, instead of the array passed in. See? OrderBy creates a new array that is sorted. The original array is not mutated.

Array.Sort mutates the array passed in, so it actually changes the arr here:

var arr = new string[] { "pink", "blue", "red", "black", "aqua" };
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Thanks for the detailed explanation. Your answer and ColeJohnson's answer made it clear what is happening to the array for me now. – niki b Aug 06 '17 at 07:03
1

In this line:

arr = (arr.OrderBy(i => i).ToArray()); 

you are creating a new array and assigning to to arr. The array you passed to the function is no longer accessible by that function. If you want to be able to modify the array arr so that other functions can use it, you need to use the ref keyword:

public static void ArrayCalledWithOrderBy(ref string[] arr)
Cole Tobin
  • 9,206
  • 15
  • 49
  • 74
0

You can use Object.ReferenceEquals to check arr's reference has been change after call arr.OrderBy(i => i).ToArray().

public static void ArrayCalledWithOrderBy(string[] origin)
   {
       var arr = (origin.OrderBy(i => i).ToArray());
       //isEqual flag is false
       var isEqual = Object.ReferenceEquals(arr, origin);

      DisplayArray(arr, "After orderby inside method");
   }
jerry
  • 317
  • 2
  • 20