在静态类方法中通过引用传递参数

Posted

技术标签:

【中文标题】在静态类方法中通过引用传递参数【英文标题】:Passing arguments by reference in static class methods 【发布时间】:2021-12-19 09:26:12 【问题描述】:

我收到错误“属性或索引器可能不会作为输出或引用参数传递” 我的任务是将冒泡排序实现为静态类,如果我将其设为非静态,它就可以正常工作。

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;
        
    

【问题讨论】:

(arr[j], arr[j + 1]) = (arr[j + 1], arr[j]); arr[j] 是一个索引器(示例)。错误信息是正确的。您断言使其成为非静态会导致其工作是错误的:see here。 【参考方案1】:

由于编译器所说的原因,你不能做你想做的事......但是你可以只使用 reference列表

private static void Swap(IList<int> list, int i)

   int temp = list[i];
   list[i] =  list[i+1];
   list[i+1] = temp;

或使用 元组解构的 1 班轮

if (arr[j] > arr[j + 1])

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

那么,我听到你问哪个更快?让我们进行基准测试....

基准测试

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

结果

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

测试代码

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


总结

这个基准测试完全是浪费时间,最好不要担心其他事情。

【讨论】:

【参考方案2】:
Indexer access returns temporary value. 'ref' argument must be an assignable variable, field or an array element

您不能从列表中发送引用,因为对特定索引元素的访问是通过索引器完成的。 see more about indexers

相反,您可以使用 ref 从数组 int[] 发送引用,因为它不是使用索引器,而是直接引用。

如果你还想用list,你可以使用C#特性来交换:

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

而不是Swap 方法

你的代码会变成

    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;
            
        
    

【讨论】:

【参考方案3】:

语言中有一个返回参考的功能。但这是为数组索引器实现的,而不是列表索引器,因为可以重新分配列表。因此,如果您将 arr 更改为数组,我希望您的示例代码能够正常工作。看这个最小的例子:

        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;
        

但是,对于大多数实际应用,我建议使用@TheGeneral 发布的解决方案之一。 ref 返回有点不寻常,可能不是所有程序员都熟悉。

【讨论】:

【参考方案4】:

在这种情况下 arr[j] 被认为是一个属性,因为它需要读取列表中的一个字段。属性不是变量。它们是方法,不能传递给 ref 参数。

如果你想这样做,你需要将数组属性传递给一个临时变量。

示例:

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;

【讨论】:

以上是关于在静态类方法中通过引用传递参数的主要内容,如果未能解决你的问题,请参考以下文章

无法在 MySQLi 中通过引用传递参数 [重复]

在 C++ 中通过引用传递时参数的默认值

如何在php中通过引用传递无限参数[重复]

如何在 Ruby 中通过引用传递?

在 C 中通过引用传递时会发生啥?

在 Python 中进行多处理时,在类实例中通过 self 传递参数