Reputation: 41
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();
}
}
}
Upvotes: 4
Views: 4503
Reputation: 678
Why don't you simply do the following:
public void Produce()
{
_queue.Clear();
_queue.Enqueue("one");
}
Upvotes: 0
Reputation: 4406
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.
Holder<T>
classOne 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.
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.
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
}
}
Upvotes: 0
Reputation: 241779
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.
Upvotes: 3
Reputation: 351698
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>
.
Upvotes: 0
Reputation: 57996
Inside your Produce()
method, remove this line:
_queue = new Queue<string>();
There you're creating a new queue instance.
Upvotes: 1
Reputation: 2132
General approach for this kind of problem....step through the code with the debugger and see what line causes the exception to be thrown.
Upvotes: -1
Reputation: 31868
Try the following:
public One(Queue<string> queue)
{
// should be assigning by reference
_queue = queue;
}
public void Produce()
{
_queue.Enqueue("one");
}
Upvotes: 1
Reputation: 13990
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)
Upvotes: 0