【问题标题】:Is C#'s Queue.dequeue() pass by reference or value?C# 的 Queue.dequeue() 是按引用传递还是按值传递?
【发布时间】:2019-05-10 16:32:11
【问题描述】:

所以我正在尝试使用队列来创建对象池以回收它们。在实现它之后,我似乎仍然获得相同级别的内存使用。我很难理解使用 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);    
}

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

【问题讨论】:

  • 对于引用类型,引用是按值传递的。使用值类型,传递值。 ref List&lt;Node&gt; nodelist 将引用传递给引用,但这无关紧要,因为您从未在方法中分配给 nodelist。 ref 在该代码中什么也不做。 “同级别垃圾”是什么意思?你能解释一下输入是什么,期望的输出是什么,以及实际输出是什么?
  • 什么是return nodePool.Dequeue().init(tile, parent, gCost, hCost);?这样做的目的是什么? init() 是做什么的,为什么要在从队列中取出的项目上调用它?
  • List.Clear() 不会将容量重置为 0,容量保持不变,因此如果 GC 不收集它,它可能会保持为垃圾。
  • @michaelmena 当您提到“垃圾级别”时,您指的是内存使用情况吗?
  • @EdPlunkett 对不起,我的意思是当垃圾收集(GC)启动时,它会删除很多对象(节点)。据我了解,GC 会在没有引用对象时从内存中删除对象,但如果我认为它们应该有引用,因为我将它们“存储”到队列中。

标签: c# queue


【解决方案1】:

当您调用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。)

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

【讨论】:

    猜你喜欢
    • 2014-04-08
    • 2016-11-28
    • 2017-11-14
    相关资源
    最近更新 更多