Home > Software engineering >  Passing arguments by reference in static class methods
Passing arguments by reference in static class methods

Time:11-06

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;
        }
    }

CodePudding user response:

You cant 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? Lets 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.

CodePudding user response:

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;
            }
        }
    }

CodePudding user response:

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.

CodePudding user response:

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;
}
  • Related