为什么引用类型参数在C#中通过引用隐式传递给函数



所以我在学习堆排序算法,我完全理解它。然而,当我查看我的代码时,我并不真正理解它为什么有效,因为在C#中,如果我想通过将参数传递给函数来更改参数的值,我必须使用ref关键字,否则它是一个值类型的参数,在传递给函数时不应更改。

public static class HeapSort
{
public static List<int> Sort(List<int> array)
{
List<int> result = new List<int>();
int n = array.Count;
for(int i = n/2 - 1; i >= 0; i--)
{
Heapify(array, n, i);
}
int sizeOfArray = n;
while(result.Count != n)
{
result.Insert(0, array[0]);
array[0] = array[sizeOfArray - 1];
array.RemoveAt(sizeOfArray - 1);
sizeOfArray--;
Heapify(array, sizeOfArray, 0);
}
return result;
}
private static void Heapify(List<int> array, int n, int i)
{
int smallest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if(left < n && array[smallest] > array[left])
{
smallest = left;
}
if(right < n && array[smallest] > array[right])
{
smallest = right;
}
if(smallest != i)
{
int temp = array[smallest];
array[smallest] = array[i];
array[i] = temp;
Heapify(array, n, smallest);
}
}
}

我不明白为什么在没有ref的情况下调用Heaify可以改变数组。

在c#中有引用类型和值类型。值类型是按值传递的,除非您使用ref关键字传递它们。引用类型总是通过引用传递的。

例如,如果您声明两个变量,如

List<int> myList = new List<int>();
int myint = 0;

myInt保持值0,但myList仅保持指向List<int>的实际实例的引用。这意味着,当您将myInt传递给一个方法时,您只需传递其值的一个副本。当您传递myList时,您会执行相同的操作——您传递其值的副本,但其值是对List<int>实例的引用,因此该引用的副本仍然引用堆上的相同List<int>

当您传递带有ref关键字的变量时,实际上您传递了一个指向原始变量的指针。如果用ref关键字传递myInt,则方法将收到指向myInt的指针,因此它将能够修改其实际值。若用ref关键字传递myList,同样的事情也会发生。现在,您的方法可以修改myList变量的实际值。它可以将其设置为其他引用。

当你在实践中看到它时,它可能更容易理解,所以这里有一个小的控制台应用程序,显示了两种方法之间的区别,这两种方法的区别仅在于使用ref关键字

static void Main(string[] args)
{
//create new instane of List<int> on the heap and store reference to it on the stack in 'myList 'variable
List<int> myList = new List<int>() { 1 };
//create new instance of int and store it on the stack in 'myInt' variable
int myint = 2;
//call MyMethod
//copy value of myInt (2) to the new stack frame and store it in 'i' variable
//copy value of myList (reference to List<int>) to the new stack frame and store it in 'list' variable
MyMethod(myint, myList);
Console.WriteLine(myint);       //prints 2
Console.WriteLine(myList[0]);   //prints 4
//call MyMethod
//inside new stack frame store pointer to 'myint' variable
//inside new stack frame store pointer to 'myList' variable
MyMethod(ref myint, ref myList);
Console.WriteLine(myint);       //prints 3
Console.WriteLine(myList[0]);   //prints 5
Console.ReadLine();
}
static void MyMethod(int i, List<int> list)
{
//store value of 3 on stack in variable 'i'
i = 3;
//use reference stored on stack in 'list' variable to find our instance of List<int> on the heap and store 4 under index 0
list[0] = 4;
//create new instane of List<int> on the heap and store reference to it on the stack, in 'list 'variable
list = new List<int>() { 5 };
}
static void MyMethod(ref int i, ref List<int> list)
{
//use pointer to 'myInt' variable to store value of 3 in 'myInt' variable
i = 3;
//use pointer to 'myList' variable to get reference stored in it and use that reference to find our instance of List<int> on the heap and store 4 under index 0
list[0] = 4;
//create new instane of List<int> on the heap and use pointer to 'myList' variable to store reference to that new instance inside 'myList' variable
list = new List<int>() { 5 };
}

最新更新