Filip Stoyanov
Filip Stoyanov

Reputation: 1

Passing arguments by reference in static class methods

I get the error "A property or indexer may not be passed as an out or ref parameter" My task is to implement buble sort as a static class if I make it non static it works just fine.

public static class BubleSort
    {
        public static void Sort(List<int> arr)
        {
            for (int i = 0; i < arr.Count-1; i++)
            {
                var flag = true;
                for (int j = 0; j < arr.Count-i-1; j++)
                {
                    if (arr[j] > arr[j + 1])
                    {
                        Swap(ref arr[j],ref arr[j + 1]);
                        flag = false;
                    }
                }

                if (flag)
                    break;
            }
        }

        private static void Swap(ref int v1,ref int v2)
        {
            int temp = v1;
            v1 = v2;
            v2 = temp;
        }
    }

Upvotes: 0

Views: 1016

Answers (4)

TheGeneral
TheGeneral

Reputation: 81503

You can't do what you are trying to do for exactly the reason the compiler says... However you could just use the reference of the list

private static void Swap(IList<int> list, int i)
{
   int temp = list[i];
   list[i] =  list[i+1];
   list[i+1] = temp;
}

Or a 1 liner using tuple deconstruction

if (arr[j] > arr[j + 1])
{
   (arr[j], arr[j + 1]) = (arr[j + 1], arr[j]);
   flag = false;
}

So, which is faster I hear you ask? Let's benchmark....

Benchmarks

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000
AMD Ryzen 9 3900X, 1 CPU, 24 logical and 12 physical cores
.NET SDK=6.0.100-rc.2.21505.57
  [Host]   : .NET 5.0.11 (5.0.1121.47308), X64 RyuJIT  [AttachedDebugger]
  .NET 5.0 : .NET 5.0.11 (5.0.1121.47308), X64 RyuJIT

Job=.NET 5.0  Runtime=.NET 5.0

Results

Method Mean Error StdDev
Tuple 3.555 ms 0.0170 ms 0.0159 ms
Classic 3.542 ms 0.0075 ms 0.0062 ms

Test code

[SimpleJob(RuntimeMoniker.Net50)]
public class Test
{
   private List<int> data;


   [GlobalSetup]
   public void Setup()
   {
      var r = new Random(42);
      data = Enumerable.Range(0,1000000).Select(x => r.Next()).ToList();
   }

   [Benchmark]
   public void Tuple()
   {
      for (int i = 0; i < data.Count-1; i++)
         Swap1(data, i);
   }

   [Benchmark]
   public void Classic()
   {
      for (int i = 0; i < data.Count-1; i++)
         Swap1(data, i);
   }

   [MethodImpl(MethodImplOptions.AggressiveInlining)]
   private static void Swap1(List<int> l, int i)
   {
      (l[i], l[i+1]) = (l[i+1], l[i]);
   }

   [MethodImpl(MethodImplOptions.AggressiveInlining)]
   private static void Swap2(List<int> l, int i)
   {
      int temp = l[i];
      l[i] =  l[i+1];
      l[i+1] = temp;
   }

}

Summary

This benchmark was a complete waste of time, best worry about other things.

Upvotes: 3

Maurice Chrisnach
Maurice Chrisnach

Reputation: 1

In this case arr[j] is considered as a property, since it needs to read a field in the List. Properties are not variables. They're methods, and cannot be passed to ref parameters.

If you want to do it this way, you would need to pass the array property to a temporary variable.

Example:

if (arr[j] > arr[j + 1]){
   int tempJ = arr[j];
   int tempJ2 = arr[j + 1];
   Swap(ref tempJ, ref tempJ2);
   arr[j] = tempJ;
   arr[j + 1] = tempJ2;
   flag = false;
}

Upvotes: 0

JonasH
JonasH

Reputation: 36371

There is a feature in the language for returning references. But this is implemented for arrays indexers, not list indexers, since lists can be re-allocated. So I would expect your example code to work if you changed arr to an array. See this minimal example:

        public void SwapTest()
        {
            var arr = new [] {1, 2};
            Swap( ref arr[0], ref arr[1]);
        }

        public static void Swap(ref int a, ref int b)
        {
            var tmp = a;
            a = b;
            b = tmp;
        }

However, for most practical applications I would suggest using one of the solutions posted by @TheGeneral. ref returns are somewhat unusual, and may not be familiar to all programmers.

Upvotes: 1

Maxian Nicu
Maxian Nicu

Reputation: 2294

Indexer access returns temporary value. 'ref' argument must be an assignable variable, field or an array element

You cannot send references from a list because access to specific index element is done through Indexer. see more about indexers

Instead, you can send references from an array int[] using ref, because it's not using indexer, but direct reference.

If you still want to use list, you can use C# feature to swap:

(arr[j],arr[j + 1]) = (arr[j + 1], arr[j]);

instead of Swap method

Your code will become

    public static class BubleSort
    {
        public static void Sort(List<int> arr)
        {
            for (int i = 0; i < arr.Count-1; i++)
            {
                var flag = true;
                for (int j = 0; j < arr.Count-i-1; j++)
                {
                    if (arr[j] > arr[j + 1])
                    {
                        (arr[j],arr[j + 1]) = (arr[j + 1], arr[j]);
                        flag = false;
                    }
                }

                if (flag)
                    break;
            }
        }
    }

Upvotes: 0

Related Questions