如何在 C 中通过引用传递#

How to pass by reference in C#

提问人:tmsh 提问时间:5/17/2019 最后编辑:Jon Skeettmsh 更新时间:5/17/2019 访问量:363

问:

我正在编写一个快速排序类,模仿 Sedgewick 的“算法 4”中给出的代码。原始代码是用 Java 编写的。我用 C# 翻译了核心部分,但它似乎不起作用。似乎问题出在了线上,但我不知道如何纠正它。a = a.OrderBy(x => r.Next()).ToArray()

我试图添加 s 和方法签名,但是一旦我调用了 中的函数,编译器就抱怨 . 我还尝试使第一个返回一个 ,即排序数组。但是,当我像这样调用它时,它会抛出一个运行时错误说refSortPartitionSort(ref a)Maincannot converte ref System.String[] to ref System.IComparable[]SortIComparable[]Mainstring[] nums = (string[]) Sort(nums)Unable to cast object of type 'System.IComparable[]' to type 'System.String[]'.

public class Quick
{
    public static void Sort(IComparable[] a)
    {
        Random r = new Random();
        a = a.OrderBy(x => r.Next()).ToArray();
        Sort(a, 0, a.Length - 1);
    }

    private static void Sort(IComparable[] a, int lo, int hi)
    {
        if (lo >= hi) return;
        int p = Partition(a, lo, hi);
        Sort(a, lo, p - 1);
        Sort(a, p + 1, hi);
    }

    private static int Partition(IComparable[] a, int lo, int hi)
    {
        int i = lo, j = hi;
        IComparable p = a[lo];
        while (true)
        {
            while (Less(a[++i], p))
            {
                if (i == hi)
                    break;
            }

            while (Less(p, a[--j]))
            {
                if (j == lo)
                    break;
            }

            if (i >= j) break;

            Exch(a, i, j);
        }
        Exch(a, lo, j);
        return j;
    }

    private static void Exch(IComparable[] a, int lo, int hi)
    {
        IComparable tmp = a[lo];
        a[lo] = a[hi];
        a[hi] = tmp;
    }

    private static bool Less(IComparable a, IComparable b)
    {
        return a.CompareTo(b) < 0;
    }


    public static void Main(string[] args)
    {
        string[] nums = File.ReadAllLines(args[0]);
        for (int i = 0; i < nums.Length; i++)
        {
            Console.WriteLine(nums[i]);
        }
        Sort(nums);
        Console.WriteLine("After sorting:");
        for (int i = 0; i < nums.Length; i++)
        {
            Console.WriteLine(nums[i]);
        }
        Console.ReadKey();
    }
}

第二个 WriteLine 应该打印出排序后的数组,但它没有。

C# 按值传递

评论

0赞 mitiko 5/17/2019
您是否尝试过添加方法调用?as IComparable<string>
6赞 Jon Skeet 5/17/2019
请您澄清一下“它似乎不起作用”,以及为什么您认为解决方案是使用引用传递?(我认为你根本不需要在这里使用......ref
0赞 tmsh 5/17/2019
@JonSkeet 通过说“它似乎不起作用”,我的意思是在调用方法后,打印出来的数组仍然是原始数组(未排序)。我不确定解决方案是什么,并认为按引用传递可能会起作用,因为该方法不返回任何内容,并且需要修改传递给该方法的数组。我对 C# 非常陌生,在C++方面只有一些有限的经验,人们可以通过引用对象来传递。Sort()MainSort()
2赞 Jon Skeet 5/17/2019
您也可以在 C# 中通过引用传递,但我认为这不是正确的方法 - 或者至少,您可能需要进行重大更改。这里最简单的解决方法可能是将随机播放部分移动到方法中 - 排序行为不需要随机播放开始......Main
0赞 tmsh 5/17/2019
@JonSkeet非常感谢您的快速修复建议,它奏效了!但是,您能否向我展示如何在不将随机播放部分移动到方法的情况下实现目标。就像我在帖子中所说的那样,我尝试将 ref 添加到签名中而不添加,但都失败了。我上面给出的代码是我对书中 Java 对应部分的直接翻译,作者将 shuffle 部分放在方法中,以便 shuffling 对假定的调用者 是盲目的。我想保留相同的逻辑。MainSort()Sort()

答:

0赞 pitprok 5/17/2019 #1

您需要返回排序后的数组,因为就像您说的那样,它是按值传递的,而不是按引用传递的。 将返回类型而不是 void 添加到 Sort 方法并返回 a;

和改变

排序(nums);

nums=排序(nums);

2赞 Sweeper 5/17/2019 #2

这里的问题不是通过引用传递,而是这一行,正如您所确定的:

a = a.OrderBy(x => r.Next()).ToArray();

您正在提供一个新值,这与仅修改 的内容不同。由于该方法对数组进行就地排序,因此不应创建新数组,并且不必在对数组进行排序之前对其进行洗牌。aaSort

因此,删除这两行应该可以使您的代码正常工作:

Random r = new Random();
a = a.OrderBy(x => r.Next()).ToArray();

当您尝试从 返回数组时,您似乎遇到了一些问题。您可以通过将所有方法设置为泛型来解决此问题,并将泛型参数限制为:SortTIComparable<T>

public static T[] Sort<T>(T[] a) where T: IComparable<T>
{
    Random r = new Random();
    a = a.OrderBy(x => r.Next()).ToArray();
    Sort(a, 0, a.Length - 1);
    return a;
}

private static void Sort<T>(T[] a, int lo, int hi) where T: IComparable<T>
{
    if (lo >= hi) return;
    int p = Partition(a, lo, hi);
    Sort(a, lo, p - 1);
    Sort(a, p + 1, hi);
}

private static int Partition<T>(T[] a, int lo, int hi) where T: IComparable<T>
{
    int i = lo, j = hi;
    T p = a[lo];
    while (true)
    {
        while (Less(a[++i], p))
        {
            if (i == hi)
                break;
        }

        while (Less(p, a[--j]))
        {
            if (j == lo)
                break;
        }

        if (i >= j) break;

        Exch(a, i, j);
    }
    Exch(a, lo, j);
    return j;
}

private static void Exch<T>(T[] a, int lo, int hi)
{
    T tmp = a[lo];
    a[lo] = a[hi];
    a[hi] = tmp;
}

private static bool Less<T>(T a, T b) where T: IComparable<T>
{
    return a.CompareTo(b) < 0;
}

评论

0赞 tmsh 5/17/2019
顺便说一句,一个与 C# 没有直接关系的问题,为什么你说数组在排序之前绝对不应该被洗牌?《算法 4》一书说,洗牌对于算法运行时间的可预测性至关重要,可以防止最坏的情况发生。
0赞 Sweeper 5/17/2019
@tmsh我明白了。它确实可以防止最坏的情况,但我往往不会在遇到性能问题之前担心它们。在这种情况下,您应该使用随机排列算法来随机排列数组,而不是返回新数组。