C#引用参数传递

时间:2009-11-04 02:15:35

标签: c# parameters ref

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

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

8 个答案:

答案 0 :(得分:3)

问题是在函数Produce中,您正在实例化一个新的Queue并将其分配给私有成员One._queue;这会覆盖您在构造函数中对One._queue所做的赋值。但是,本地_queue引用了您从未加入的其他Queue<string>

您可以通过移除Produce的第一行来解决此问题,以获取以下内容:

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

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

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

当您希望main中的本地_queueQueue<string>构造函数执行后引用One的不同实例时,将使用按引用传递。

答案 1 :(得分:1)

尝试以下方法:

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

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

答案 2 :(得分:1)

Produce()方法中,删除以下行:

_queue = new Queue<string>();

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

答案 3 :(得分:0)

one.Produce()用新的“空”队列()替换_queue。 这就是为什么字符串值= _queue.Dequeue()抛出错误(b / c _queue)

答案 4 :(得分:0)

Produce方法中删除此行:

_queue = new Queue<string>();

这会消除您通过引用传入的现有Queue<String>的引用,并引用新的Queue<String>

答案 5 :(得分: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 - 指针

另一种解决方案是使用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
    }
}

答案 6 :(得分:0)

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

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

答案 7 :(得分:-1)

解决此类问题的一般方法....使用调试器逐步执行代码,并查看引发异常的行。