4

我有一段代码不起作用,我会很感激你们可以为我提供的任何帮助

下面的代码正在生成一个异常......但我认为它不应该,除非我误解了 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();
        }
    }
}
4

8 回答 8

3

问题是在函数中Produce您正在实例化一个新Queue成员并将其分配给私有成员One._queueOne._queue这会覆盖您在构造函数中所做的分配。但是,本地_queue是指Queue<string>您从未入队的不同。

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

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

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

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

当您希望 main 中的 local 在构造函数执行后_queue引用不同的实例时,将使用通过引用传递。Queue<string>One

于 2009-11-04T02:19:45.137 回答
1

尝试以下操作:

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

    public void Produce()
    {
        _queue.Enqueue("one");
    }
于 2009-11-04T02:20:24.387 回答
1

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

_queue = new Queue<string>();

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

于 2009-11-04T02:21:08.630 回答
0

问题

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

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

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

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


解决方案 A -Holder<T>

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

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

然后,One该类将在一个Holder<Queue<string>>对象上工作。该Main方法也必须继续使用Holder<Queue<string>>。这样,One可以更改队列实例并将其反映在Main.

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


解决方案 B - 指针

另一种解决方案是使用指针。完全可以在 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
    }
}
于 2009-11-04T03:14:39.347 回答
0

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

于 2009-11-04T02:18:57.767 回答
0

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

_queue = new Queue<string>();

这消除了对Queue<String>您通过引用传入的现有引用以及对新的引用的新引用Queue<String>

于 2009-11-04T02:21:40.413 回答
0

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

public void Produce()
{
    _queue.Clear();
    _queue.Enqueue("one");
}
于 2012-02-03T20:25:02.940 回答
-1

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

于 2009-11-04T02:20:55.440 回答