Question

I was working on a method yesterday and ran into something strange, here is dumbed down version of the code: Basically the problem is that the OrderBy applied in the Bar.PopulateList method is not persisting.


class Foo
{
    List MyObjects;

    public void PopulateMyObjects()
    {
        //Items are added to my list but the OrderBy is not persisting.
        Bar.PopulateList(MyObjects);
    }
}

class Bar
{
   public static int PopulateList(List theList)
   {
       foreach(var in WebSerbiceCall)
       {
            theList.Add(var);
       }
       // the OrderBy call only sorts 'theList' in the context of this method.
       // When I return from this method theList has been populated but the Ordering has 
       // reverted back to the order that the items were added to the list.
       theList.OrderBy(obj => obj.ID);
       return theList.Count;
   }
}

Now if I update the code and add the ref keyword as per below it all works: e.g. public static int PopulateList(ref List theList) and Bar.PopulateList(ref MyObjects);

Can anyone enlighten me? I thought objects were always passed by ref? Is it the fact that OrderBy is an extension method?

Thanks, Cian

Was it helpful?

Solution

The problem here is that the OrderBy call does not actually mutate theList in any way. It instead returns a new IEnumerable<object> which is ordered. Hence this is why you do not see the affects of the call outside the method, it's simply not changing the object.

Using the OrderBy method creates a new value and hence if you want the calling function to be aware of this new value it must be returned in some manner. The most common places are in the return value or in a ref/out param.

public static int PopulateList(ref List<object> theList) {
  ...
  theList = theList.OrderBy(obj => obj.ID).ToList();
}

OTHER TIPS

Try:

return theList.OrderBy(obj => obj.ID).Count;

(I was going to add an explaination but @jaredPar has explained it)

C# passes arguments by value, it's just that the value of a reference type is the pointer to it's memory location. The problem you are having is this line:

theList.OrderBy(obj => obj.ID);

You're not assigning the result:

theList = thisList.OrderBy(obj => obj.ID).ToList();

The Enumerable.OrderBy extension method does not sort the List<T> in-place. It returns an IEnumerable<T> that returns the elements of the list in sorted order.

Use the List<T>.Sort method to sort the List<T> in-place.

If you don't use the ref keyword then the parameter passed is a new reference to the same object. It is 'passed by reference' in a sense but you have to think about it a bit differently.

Other answers are correct, OrderBy does not perform in place and instead returns an ordered collection. But if you set your parameter to the result, then you change the value of the parameter (the reference) to point to the new collection, rather than changing the underlying object itself.

For example,

theList = thisList.OrderBy(obj => obj.ID).ToList();

takes theList, orders it and then creates a new List. Then the value of theList - which is a reference to a list - is changed to point to the newly created (ordered) list. The original reference, created outside this method, still points to the original unordered list.

The reason is that whenever you call .ToList() you actually create a new list. When you use the ref keyword, you pass the actual variable containing the reference to the list, rather than create a new reference to the same list.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top