【问题标题】: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

标签: c# class static ref


【解决方案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;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-02-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-04-06
          • 1970-01-01
          • 2011-09-17
          相关资源
          最近更新 更多