首页 > 解决方案 > C# 的 Queue.dequeue() 是按引用传递还是按值传递?

问题描述

所以我试图使用队列来创建一个对象池以回收它们。在实现它之后,我似乎仍然获得相同级别的内存使用。我很难理解使用 queue() 或 dequeue() 是按引用传递还是按值传递。

void ClearNodeListandBackupToPool(ref List<Node> nodelist)    
{    
    foreach (var node in nodelist)    
    {    
        nodePool.Enqueue(node);    
    }

    nodelist.Clear();    
}

Node CreateNewNode(Vector2 tile, Node parent, double gCost, double hCost)    
{    
    if (nodePool.Count > 0)    
        return nodePool.Dequeue().init(tile, parent, gCost, hCost);    

    return new Node(tile,parent,gCost,hCost);    
}

我预计这种方法的内存使用率会很低或没有,但它保持不变。感谢您的帮助!:)

标签: c#queue

解决方案


当您调用Dequeue时,返回的是一个值,该值是对返回对象的引用。在解释这意味着什么之前,更容易说明为什么它可能对您无关紧要:

如果你这样做:

var list = new List<Object>();
list.Add(new object());
var o = list[0];

现在有两个对同一个对象的引用。list有一个参考,o还有另一个参考。如果o超出范围但list没有,那么该对象将不会被垃圾收集,因为仍然存在对它的引用。

如果你这样做:

var queue = new Queue<Object>();
queue.Enqueue(new object());
var o = queue.Dequeue();

只有一个对队列中对象的引用。为什么?因为一旦你将它出列,该项就已经从队列中移除了,所以队列中不再有对它的引用。o是唯一的引用,如果o超出范围,对象可以被垃圾回收,因为不再有任何引用。

对象是按值返回还是按引用返回的问题并不重要。

引用按值返回是什么意思?

在上面使用列表的示例中,list[0]返回一个值,该值是对对象的引用。如果我们这样做:

var list = new List<Object>();
list.Add(new object());
var o = list[0];
o = "x";

...o现在指的是一个字符串,但列表中的项目没有改变。它没有被“x”取代。这是因为从列表中检索项目返回了一个值,即对该对象的引用的副本。我们还没有检索到实际参考。所以o = "x";替换从列表返回的引用的副本,而不是列表中包含的实际引用。

其他人观察到——我同意——理解这种行为比用语言表达要容易得多。这些例子很容易理解,但对正在发生的事情的描述却不是。

C# 7 引入了ref returns. 这允许方法通过引用返回对象。这意味着该方法返回它自己的引用。很容易发现,因为ref在调用方法时会使用关键字,例如ref var object = ref queue.Dequeue(). (你不能这样做,因为Dequeue不使用ref returns。)

我之所以将其包括在内,是因为似乎有必要逐字回答您的问题。出于实际目的,您可能在需要它之前并不关心它。我毫不怀疑添加它是有原因的,但我也没有使用它。


推荐阅读