4

i have a piece of code that isn't working, i'd appreciate any help you guys can provide me

the code below is generating an exception ... but i'd think it shouldn't, unless i'm misinterpreting the ref semantics.

EDIT: thanks for all the answer ... i know i'm instatiating a new Queue object in the One.Produce method ... but this is what i actually want to do, i would like for Main._queue to hold a reference to One._queue. Is that possible at all ?

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();
        }
    }
}
3
  • The exception it generates would be very helpful ... Commented Nov 4, 2009 at 2:18
  • 2
    To respond to your edit, you are saying that you want to instantiate a new instance of Queue<string> in One.Produce but you want the local _queue in Main to refer to that object too. In that case you either need to change the signature of Produce to be Produce(ref Queue<string> queue) and add the line queue = _queue = new Queue<string>() or you need to have Produce return a reference to the instance of Queue<string> that it creates and set _queue = one.Produce() in Main. Commented Nov 4, 2009 at 2:35
  • Returning a reference would be the easiest, or change ref to out. Commented Nov 4, 2009 at 2:38

8 Answers 8

3

The problem is that in the function Produce you are instantiating a new Queue and assigning this to the private member One._queue; this overwrites that assignment that you made to One._queue in the constructor. However, the local _queue refers to a different Queue<string> on which you never enqueued.

You can fix this by removing your first line of Produce to obtain the following:

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

By the way, you don't need to pass _queue by reference into the constructor to achieve what you are trying to achieve. That is, the following will work too

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

Passing by reference would be used when you want the local _queue in main to refer to a different instance of Queue<string> after the One constructor executes.

Sign up to request clarification or add additional context in comments.

3 Comments

thanks .. the thing is that i want Main._queue to be updated ... i know that i'm instatiating a new Queue object, but that's exactly what i want to do ... i'll edit the main post to better reflect this
If you pass _queue into the constructor of One and assign it to One._queue then the local _queue in main and One._queue will refer to the same object. Passing by reference is not needed to achieve this. The problem that you encountered is that you then set One._queue to refer to a different object with the assignment _queue = new Queue<string>(); whereas the local _queue in Main still referred to the initial instance of Queue<string>.
jason, pls check my answer below ... too long for comments
1

Try the following:

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

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

Comments

1

Inside your Produce() method, remove this line:

_queue = new Queue<string>();

There you're creating a new queue instance.

Comments

0

one.Produce() replaces the _queue with a new "empty" Queue(). That's why string value = _queue.Dequeue() is throwing an error (b/c the _queue)

Comments

0

Remove this line from your Produce method:

_queue = new Queue<string>();

This is wiping out the reference to the existing Queue<String> that you passing in by reference with a new reference to a new Queue<String>.

Comments

0

The Problem

The problem is that the ref keyword will only affect the method it was declared. So, you would be able to change Main's queue in One's constructor, but not in the Produce method.

In your code, One is creating a second reference to the same queue object. The Produce method is simply changing this second reference.

In other words, when you make a call to a method passing a parameter by reference, the called method will be able to change the referenced object, but only at this call site. After the call returns, whatever object is referenced by the _queue variable at the Main method will remain there.

One other consideration. The assignment at One's constructor (where you commented "should be assigning by reference") works in exactly the same way as it would work if the ref keyword wasn't there. Only if the assignment was inverted to queue = _queue; then the ref keyword would make a difference.


Solution A - Holder<T> class

One solution that might make sense would be to create a Holder class like this:

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

Then, the One class would work on a Holder<Queue<string>> object. The Main method would have to work on Holder<Queue<string>> too. This way, One could change the queue instance and it would be reflected in Main.

Ok, ok. I know this solution is very awkward, but the requirements for your question seems awkward to me too. Is it a rhetorical question? Or are you trying to solve a bigger problem here? If it's the latter, I'm curious to know what is this bigger problem.


Solution B - pointers

Another solution would be to use pointers. It's perfectly possible to be done in C#, but not at all recommended. If you are interested, I can try to write a solution using pointers.


Solution C - Single position array

Your own answer gave me the idea for a third possible solution. It's intrinsically very similar to the other two solutions. Instead of explaining it in English, let me show it in 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
    }
}

3 Comments

jpbochi, yes i'm working on a bigger problem .. pls check answer below ... too long for comments
I read your answer carefully and it gave me the idea for a 3rd solution. Plz, check it out.
I wrote some more consideration at the start of the answer too. BTW, is it possible to you to provide us a bigger picture of the problem you're trying to solve?
0

Why don't you simply do the following:

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

1 Comment

the .Clear() function for the Queue has the effect of basically creating a new Queue without creating a new reference. This way you still keep the same reference in both Main and One and it becomes empty.
-1

General approach for this kind of problem....step through the code with the debugger and see what line causes the exception to be thrown.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.