tags:

views:

391

answers:

9

hi, 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();
        }
    }
}
A: 

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)

Nestor
+1  A: 

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.

Jason
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
lboregard
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
jason, pls check my answer below ... too long for comments
lboregard
+1  A: 

Try the following:

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

    public void Produce()
    {
        _queue.Enqueue("one");
    }
C. Ross
A: 

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

NeilDurant
+1  A: 

Inside your Produce() method, remove this line:

_queue = new Queue<string>();

There you're creating a new queue instance.

Rubens Farias
A: 

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>.

Andrew Hare
A: 

@jason

i see ... but i was led to believe that i could assign a new object to One._queue, making Main._queue refer to this new object

please check code below, directly from msdn (http://msdn.microsoft.com/en-us/library/0f66670z%28VS.71%29.aspx)

using System;
class PassingRefByRef 
{
   static void Change(ref int[] arr)
   {
      // Both of the following changes will affect the original variables:
      arr[0]=888;
      arr = new int[5] {-3, -1, -2, -3, -4};
      Console.WriteLine("Inside the method, the first element is: {0}", arr[0]);
   }

   public static void Main() 
   {
      int[] myArray = {1,4,5};
      Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", myArray [0]);
      Change(ref myArray);
      Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", myArray [0]);
   }
}
lboregard
No, when you set `One._queue` to refer to a new instance of `Queue<string>` it has no impact on what the local `_queue` in `Main` refers to. A reference is just something that lets you refer to an object. When you construct `One` and set `One._queue = queue` where `queue` is the parameter to the constructor, you now have `One._queue` and the local `_queue` in Main referring to the same object. But then in `Produce` when you set `One._queue = new Queue<string>();` you are now setting `One._queue` to refer to a different object. But you aren't changing what the local `_queue` in Main refers to.
Jason
ok, i will have to work around this ... thanks for your help
lboregard
I already explained how to work around this in a comment to your initial post. Please let me know if you have questions.
Jason
no, thanks ... this is just sample code to explain the issues i was having ... in my project i need to look into one of two approaches you mentiones .. thanks a lot !!!
lboregard
A: 

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
    }
}
jpbochi
jpbochi, yes i'm working on a bigger problem .. pls check answer below ... too long for comments
lboregard
I read your answer carefully and it gave me the idea for a 3rd solution. Plz, check it out.
jpbochi
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?
jpbochi
A: 

jpbochi, yes i'm trying to solve a bigger problem, it's a producer/consumer thing

i have a class with a queue as one of it's fields ... this class instantiates two classes, one is a producer and the other is a consumer which need to work on the same queue ... the thing is the producer works in sets of items that need to be consumed .. that is

main class
- queue

producer
- queue
- queue1
- queue2

consumer
- queue

Consumer needs to consume from producer.queue1, but it can be stopped in the middle of consuming from producer.queue1 and asked to start consuming from producer.queue2

so i thought having main.queue as the pointer to which both producer and consumer were referring to and, in producer ... do producer.queue = producer.queue1 ... actually changing the reference in consumer.queue, via main.queue ... does it make sense ? :)

lboregard