我正在尝试使用传递给.NET中的函数的数组做一些事情,我有点困惑。数组是引用类型,因此对传递给函数的数组所做的更改在函数外部是可见的。示例
static void Main(string[] args)
{
byte[] arr = new byte[] { 1,2, 3, 4, 5 };
Console.WriteLine(string.Join("", arr)); //console output: 12345
doSomething(arr);
Console.WriteLine(string.Join("", arr)); //console output: 52341
}
static void doSomething(byte[] array)
{
byte tmp = array[0];
array[0] = array[array.Length - 1];
array[array.Length - 1] = tmp;
}
所以它与使用“ref”关键字(相同的控制台输出)完全相同
doSomething(ref arr); for static void doSomething(ref byte[] array)
但是,如果我在我的函数中添加以下行:
array = (new byte[] { 1 }).Concat(array).ToArray(); //size of array is changed
结果不同:
12345
52341// "ref" keyword is not used
和
12345
152341 "ref" keyword is used
有人可以解释一下为什么结果会有所不同吗?
答案 0 :(得分:2)
值类型变量是包含值的变量。 arr是指向内存中byte []实例的对象变量。当你通过值传递给方法doSomething时,你传递一个指向内存中byte []实例的指针。这样,arr和array都指向内存中byte []的相同实例。如果DoSomething改变了byte []的实例,arr和array都指向它,它实际上并没有改变变量arr,因为它仍然指向内存中的相同位置。但是,由于arr仍然指向内存中的相同位置并且该位置的实例已更新,因此arr可以“看到”更改。
当你调用Concat时,它会在内存中的其他地方生成一个新的byte []实例,并将变量数组添加到内存中的新实例中。字节[]的旧实例仍然存在,而arr仍然指向它。
当您通过ref传递变量 arr 时,对 array 指向的位置的任何更改也会影响 arr 的位置指点。当它没有被ref 传递时,DoSomething 只能更改 arr 指向的内存中byte []的实例,但它不能改变 arr 指着。
这就是为什么在按引用传递对象和按值传递对象之间存在差异。
答案 1 :(得分:0)
对于在没有Ref
关键字的方法中传递的任何参数 - 创建局部变量并表示原始参数值的副本。因此,当您传入引用类型变量arr
时 - 这实际上是Int32引用,它指向某个地址,例如ADDR。然后在方法中创建此变量的副本,该变量完全不受原始参数(arr
)的约束。但仍然指向内存中的相同地址,您仍然可以更改基础原始数据。当您通过分配= new ...
更改参考值本身时,参考值的本地副本已更改,但此更改不会影响方法中传递的原始引用。
如果您要“绑定”原始引用和new()
方法中创建的新引用 - 请使用Ref
,指定Ref
您说“我想通过引用/引用“。
以下行:
array = (new byte[] { 1 }).Concat(array).ToArray();
Ref
参数:引用已更改但由于它是通过引用传递的(Ref
) - 原始引用也会受到影响