我试图了解在c#中通过ref传递方法params而不是通过值传递参数的好处。
我有一个自定义对象的列表(大约50k(,我需要对它的属性运行一些操作。我已经编写了一个计算类,它接受50K元素的列表并返回值。我想知道,如果我通过引用传递参数,当我传递引用而不是传递50k列表的副本时,它会在运行时保存我的系统内存吗?.NET实际上是如何维护这一点的?
main(){
var itemList={}//list containing 50k items
var result=Calculate(itemList);// it passes a copy of the array
var resultByRef=Calculate(ref itemList); //**it passes address of result variable, is it going to take less memory in runtime??**
}
private int Calculate(List<CustomClass> itemList){
//do some calculation
return result;
}
private int CalculateByRef(ref List<CustomClass> itemList){
//do some calculation
return result;
}
看起来你和我一样来自C++背景。
在C#中,每个对象都被传递,但它的引用始终存在,这意味着无论对象有多大,你都会将它的引用传递给方法。
ref
关键字所做的唯一区别是使您能够更改引用本身。让我们以一个例子来理解:
static void callByRef(ref byte[] buff)
{
buff[0] = 10;
buff = new byte[5];
}
static void callNormally(byte[] buff)
{
buff[0] = 10;
buff = new byte[5];
}
static void Main(string[] args)
{
byte[] param = new byte[5];
param[0] = 5;
Console.WriteLine("Original param.Hash: " + param.GetHashCode());
callNormally(param);
Console.WriteLine("param[0]: " + param[0]);
Console.WriteLine("param.Hash: " + param.GetHashCode());
callByRef(ref param);
Console.WriteLine("param[0]: " + param[0]);
Console.WriteLine("param.Hash: " + param.GetHashCode());
return;
}
输出如下:
Origenal param.Hash: 58225482
param[0]: 10
param.Hash: 58225482
param[0]: 0
param.Hash: 54267293
在正常调用中,也可以更改对象内部的内容,但在ref
调用的情况下,可以更改对象本身。
在您的情况下,您只担心在将大数据作为参数传递给方法的情况下进行内存复制,这在C++的情况下会发生。在C#中,情况并非如此。
通过引用传递对您没有帮助
这是因为通过引用类型列表的值传递列表或数组仍然传递引用。不同的是,当您按值传递时,您传递引用的副本(值(,但复制的只是引用。通过引用传递时,传递的是原始变量。
仅仅复制一个20字节的引用与实际传递引用所需的操作没有什么不同,因此没有性能优势。只有当您需要更改变量本身时,通过引用传递才有用:例如,为其分配一个全新的List对象
//pass itemList by value
private int Calculate(List<CustomClass> itemList)
{
itemList[0] = new CustomClass(); // this still works!
//The List reference that was passed to this method still refers
// to the *same List object* in memory, and therefore if we update
// the item at position [0] here it will still be changed after the
// method returns.
// But this does NOT change the original after the method ends,
// because itemList is a different variable and we just changed
// it to refer to a whole new object.
itemList = new List<CustomClass>();
// If we had instead passed this by reference, then the calling code
// would also see the brand new list.
}
让我们完成以下示例。
static void Main(string[] args)
{
var myLocalList = new List<int> { 1, 2, 3 }; // 1
myLocalList.ForEach(x => Console.WriteLine(x)); // 2
Calculate1(myLocalList); // 3
myLocalList.ForEach(x => Console.WriteLine(x)); // 5
Calculate2(ref myLocalList); // 6
myLocalList.ForEach(x => Console.WriteLine(x)); // 8
}
private void Calculate1(List<int> list)
{
list = new List<int> { 4, 5, 6 }; // 4
}
private void Calculate2(ref List<int> list)
{
list = new List<int> { 7, 8, 9 }; // 7
}
- 步骤1创建一个用值1、2和3初始化的整数的本地列表
- 步骤2打印列表。控制台输出在单独的行中显示1、2和3
- 步骤3使用本地列表作为参数输入来调用CCD_ 3
- 步骤4为
list
变量分配一个值为4、5和6的新整数列表 - 步骤5打印列表。控制台输出在单独的行中显示1、2和3,与步骤2相同
- 步骤6调用
Calculate2
,并将本地列表作为ref参数输入 - 步骤7为
list
变量分配一个值为7、8和9的新整数列表 - 步骤8打印列表。这一次,控制台输出在单独的行中显示7、8和9
当myLocalList
传递给Calculate1
时,不会复制列表。要绝对清楚,我的具体意思是myLocalList
的内容被NOT复制到list
参数。然而,复制的是对myLocalList
的引用。换句话说,对myLocalList
的引用通过值复制到list
参数中。当步骤4将list
设置为新的4-5-6列表时,复制的引用(即list
(被修改,而不是原始引用(即,myLocalList
(。
这种情况随着Calculate2
而改变。在这种情况下,对myLocalList
的引用通过对list
参数的引用传递。这有效地将list
变成了myLocalList
的别名,这意味着当步骤7将list
设置为新的7-8-9列表时,原始引用(即myLocalList
(被修改。这就是为什么在步骤8中输出会发生变化。
。。。当我通过引用而不传递5万名单的副本?
否。Calculate
和CalculateByRef
方法都不会接收itemList
的深层副本,因此性能不会受到您建议的影响。在CalculateByRef
中使用ref
关键字传递参数,只需允许您从Calculate1
1内部修改Main
中itemList
变量的值。
根据您所展示的内容,在这种情况下,听起来不需要ref
关键字。
HTH-