在C#中,通过ref传递值有什么好处

  • 本文关键字:通过 ref c#
  • 更新时间 :
  • 英文 :


我试图了解在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万名单的副本?

否。CalculateCalculateByRef方法都不会接收itemList的深层副本,因此性能不会受到您建议的影响。在CalculateByRef中使用ref关键字传递参数,只需允许您从Calculate11内部修改MainitemList变量的值。

根据您所展示的内容,在这种情况下,听起来不需要ref关键字。

HTH-

最新更新