Extension method not changing the list when sorting it

81 Views Asked by At

I have a public static class listing my extension methods:

public static void OrderInts (this List<int> ints) 
{ 
    ints= ints.OrderBy(c => c).ToList(); 
} 
public static void AddInt(this List<int> ints) 
{ 
    ints.Add(34); 
} 
public static void AddIntAndOrder(this List<int> ints) 
{ 
    ints.Add(34); 
    ints = ints.OrderBy(c => c).ToList(); 
} 
public static void OrderAndAddInt(this List<int> ints) 
{
        ints = ints.OrderBy(c => c).ToList();
        ints.Add(34);
}

Then if I test them, I notice that:

OrderInts does not modify the list of ints.

AddInt adds an item to the list.

AddIntAndOrder adds an item to the list but does not sort it.

OrderAndAddInt does not modify the list.

Any idea why that strange behavior?

PS: If I use a static Util class:

public static void ReorderInts(List<int> ints)
{
    ints = ints.OrderBy(c => c).ToList();
}  
public static void ReorderIntsByRef(ref List<int> ints)
{
    ints = ints.OrderBy(c => c).ToList();
}

then Util.ReorderInts(myVar) does not change the list but Util.ReorderIntsByRef(ref myVar) does.

3

There are 3 best solutions below

0
Mushroomator On

It's expected behavior and for good reason. Now you have to know two things:

By default, arguments in C# are passed to functions by value. That means a copy of the variable is passed to the method.

When you pass a reference type by value:

  • If the method assigns the parameter to refer to a different object, those > changes aren't visible from the caller.
  • If the method modifies the state of the object referred to by the > parameter, those changes are visible from the caller.

Source: Microsoft documentation on Method parameters.

What your doing e.g. in OrderInts() is passing a reference type (List<T>) to a method by value (since that's the default) and then within that method you create a new List i. e. a new reference with ToList() and assign it to the method parameter ints.

When you pass the parameter to the method, a copy of that reference is created therefore within the method you only assign a new reference to that copy, the reference outside of the function remains unchanged and still points to the old list, therefore no change is observable from outside the method.

To make it work you'd have to return the new list and then you can observe the change on the new list.

public static List<int> OrderInts(this List<int> ints)
{
    return ints.OrderBy(c => c).ToList();
}

Theoretically you could also use the following which is passing a reference type by reference:

public static void OrderInts(ref List<int> ints)
{
     ints = ints.OrderBy(c => c).ToList();
}

But in general that wouldn't be a good idea because then this method has side-effects which can make code prone to error and hard to debug if you use it without good reason and all over the place.

1
Legion On

It happens in C# classes since they're reference types. So, when you pass it, the function receives a copy of the list List<int> ints. The list ints you assign in your functions, it's just creating a new local reference. Any changes aren't visible once the extension method exits.

You can use ref keyword before/after this if you want to edit the original reference.

Adding the ref modifier indicates that the first argument itself is passed by reference.

Extension Methods (C# Programming Guide)

example

public static void OrderInts(ref List<int> ints) 
{ 
    ints = ints.OrderBy(c => c).ToList(); 
}

hope it's helps ;)

0
Suryateja KONDLA On

your methods don't modify the original list because they assign a new list to the parameter, which doesn't affect the original list outside the method Use ints.Sort() in your OrderInts method to sort the list in place, directly modifying the original list

public static void OrderInts(this List<int> ints)
{ 
    ints.Sort();
}

using ref for this purpose is generally not recommended. It's better to modify the list in place or return a new sorted list from the method

public static List<int> ReorderInts(List<int> ints)
{
    return ints.OrderBy(c => c).ToList();
}