【问题标题】:C# Reference parameter passingC# 参考参数传递
【发布时间】:2010-12-12 21:11:43
【问题描述】:

我有一段代码不工作,如果你们能提供任何帮助,我将不胜感激

下面的代码正在生成一个异常......但我认为它不应该,除非我误解了 ref 语义。

编辑:感谢所有答案...我知道我正在 One.Produce 方法中创建一个新的 Queue 对象...但这是我真正想要做的,我希望 Main._queue持有对 One._queue 的引用。 这可能吗?

using System;
using System.Collections.Generic;

namespace ConsoleApplication2
{
    class One
    {
        Queue<string> _queue;

        public One(ref Queue<string> queue)
        {
            // should be assigning by reference
            _queue = queue;
        }

        public void Produce()
        {
            _queue = new Queue<string>();
            _queue.Enqueue("one");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Queue<string> _queue = new Queue<string>();

            One one = new One(ref _queue);
            one.Produce();

            // this generates an exception, since _queue is empty
            // i'd have thought _queue would have one item, "one"
            string value = _queue.Dequeue();
        }
    }
}

【问题讨论】:

  • 它生成的异常会很有帮助...
  • 为了响应您的编辑,您说您想在One.Produce 中实例化Queue&lt;string&gt; 的新实例,但您希望Main 中的本地_queue 引用该对象也。在这种情况下,您需要将Produce 的签名更改为Produce(ref Queue&lt;string&gt; queue) 并添加queue = _queue = new Queue&lt;string&gt;() 行,或者您需要让Produce 返回对它创建并设置@ 的Queue&lt;string&gt; 实例的引用987654331@Main.
  • 返回引用是最简单的,或者将 ref 更改为 out。

标签: c# parameters ref


【解决方案1】:

问题在于,在函数Produce 中,您正在实例化一个新的Queue 并将其分配给私有成员One._queue;这会覆盖您在构造函数中对One._queue 所做的分配。但是,本地 _queue 指的是另一个您从未在其中排队的 Queue&lt;string&gt;。

您可以通过删除Produce 的第一行来解决此问题,以获得以下信息:

public void Produce() {
        _queue.Enqueue("one");
}

顺便说一句,您不需要通过引用将_queue 传递给构造函数来实现您想要实现的目标。也就是说,以下也可以工作

public One(Queue<string> queue) {
    _queue = queue;
}

当您希望 main 中的本地 _queue 在 One 构造函数执行后引用 Queue&lt;string&gt; 的不同实例时,将使用按引用传递。

【讨论】:

  • 谢谢 .. 问题是我想要更新 Main._queue ......我知道我正在创建一个新的 Queue 对象,但这正是我想要做的......我'将编辑主帖以更好地反映这一点
  • 如果您将_queue 传递给One 的构造函数并将其分配给One._queue,那么main 和One._queue 中的本地_queue 将引用同一个对象。不需要通过引用传递来实现这一点。您遇到的问题是,您随后将One._queue 设置为使用分配_queue = new Queue&lt;string&gt;(); 引用不同的对象,而Main 中的本地_queue 仍然引用Queue&lt;string&gt; 的初始实例。
  • 杰森,请在下面检查我的答案...对于 cmets 来说太长了
【解决方案2】:

尝试以下方法:

    public One(Queue<string> queue)
    {
        // should be assigning by reference
        _queue = queue;
    }

    public void Produce()
    {
        _queue.Enqueue("one");
    }

【讨论】:

    【解决方案3】:

    在您的 Produce() 方法中,删除此行:

    _queue = new Queue<string>();
    

    您正在创建一个新的队列实例。

    【讨论】:

      【解决方案4】:

      one.Produce() 将 _queue 替换为新的“空”Queue()。 这就是为什么 string value = _queue.Dequeue() 会引发错误(b/c _queue)

      【讨论】:

        【解决方案5】:

        从您的 Produce 方法中删除此行:

        _queue = new Queue<string>();
        

        这将消除对现有Queue&lt;String&gt; 的引用,您通过引用传递给新Queue&lt;String&gt; 的新引用。

        【讨论】:

          【解决方案6】:

          问题

          问题是ref 关键字只会影响它声明的方法。因此,您可以在One 的构造函数中更改Main 的队列,但不能在Produce 方法中。

          在您的代码中,One 正在创建对同一队列对象的第二个引用。 Produce 方法只是更改了第二个引用。

          换句话说,当您调用通过引用传递参数的方法时,被调用的方法将能够更改引用的对象,但仅限于此调用站点。调用返回后,_queue 变量在 Main 方法中引用的任何对象都将保留在那里。

          另一个考虑因素。 One 的构造函数(您在其中评论“应该通过引用分配”)的赋值与 ref 关键字不存在时的工作方式完全相同。仅当分配反转为 queue = _queue; 时,ref 关键字才会有所作为。


          解决方案 A - Holder&lt;T&gt; 类

          一种可能有意义的解决方案是创建一个像这样的Holder 类:

          public class Holder<T> {
              public T { get; set; }
          }
          

          然后,One 类将作用于Holder&lt;Queue&lt;string&gt;&gt; 对象。 Main 方法也必须在 Holder&lt;Queue&lt;string&gt;&gt; 上工作。这样,One 可以更改队列实例,并将其反映在 Main 中。

          好的,好的。我知道这个解决方案很尴尬,但你的问题的要求对我来说似乎也很尴尬。这是一个反问句吗?或者你想在这里解决一个更大的问题?如果是后者,我很想知道这个更大的问题是什么。


          解决方案 B - 指针

          另一种解决方案是使用pointers。完全可以在 C# 中完成,但完全不推荐。如果你有兴趣,我可以尝试用指针写一个解决方案。


          解决方案 C - 单位置数组

          您自己的回答让我想到了第三种可能的解决方案。它本质上与其他两种解决方案非常相似。与其用英文解释,不如用 C# 来展示。

          class One
          {
              Queue<string>[] queueArray;
          
              public One(Queue<string>[] queueArray)
              {
                  if (queueArray == null) throw new ArgumentNullException("queueArray");
                  if (queueArray.Length != 1) throw new ArgumentException("queueArray must have one and only one item");
                  this.queueArray = queueArray;
              }
          
              public void Produce()
              {
                  queueArray[0] = new Queue<string>();
                  queueArray[0].Enqueue("one");
              }
          }
          
          class Program
          {
              static void Main(string[] args)
              {
                  var queueArray = new Queue<string>[] { new Queue<string>() };
          
                  One one = new One(queueArray);
                  one.Produce();
          
                  string value = queueArray[0].Dequeue(); //this line sets "one" to value
              }
          }
          

          【讨论】:

          • jpbochi,是的,我正在解决一个更大的问题..请检查下面的答案...对于 cmets 来说太长了
          • 我仔细阅读了您的回答,它给了我第三个解决方案的想法。请检查一下。
          • 我在答案的开头也写了一些更多的考虑。顺便说一句,您是否可以向我们提供您正在尝试解决的问题的更大图景?
          【解决方案7】:

          您为什么不简单地执行以下操作:

          public void Produce()
          {
              _queue.Clear();
              _queue.Enqueue("one");
          }
          

          【讨论】:

          • 队列的.Clear() 函数具有基本上创建一个新队列而不创建新引用的效果。这样,您仍然在 Main 和 One 中保留相同的引用,并且它变为空。
          【解决方案8】:

          此类问题的一般方法....使用调试器单步执行代码,看看是哪一行导致抛出异常。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-03-13
            • 1970-01-01
            • 1970-01-01
            • 2019-12-28
            • 1970-01-01
            • 2014-03-05
            • 2011-12-15
            相关资源
            最近更新 更多