views:

108

answers:

3

Here is a much simplified version of what I am trying to do

static void Main(string[] args)
{
    int test = 0;
    int test2 = 0;
    Test A = new Test(ref test);
    Test B = new Test(ref test);
    Test C = new Test(ref test2);
    A.write(); //Writes 1 should write 1
    B.write(); //Writes 1 should write 2
    C.write(); //Writes 1 should write 1
    Console.ReadLine();
}
class Test
{
    int _a;
    public Test(ref int a)
    {
        _a = a; //I loose the reference here
    }
    public void write()
    {
        var b = System.Threading.Interlocked.Increment(ref _a);
        Console.WriteLine(b);
    }
}

In my real code I have a int that will be incremented by many threads however where the threads a called it will not be easy to pass it the parameter that points it at the int(In the real code this is happening inside a IEnumerator). So a requirement is the reference must be made in the constructor. Also not all threads will be pointing at the same single base int so I can not use a global static int either. I know I can just box the int inside a class and pass the class around but I wanted to know if that is the correct way of doing something like this?

What I think could be the correct way:

static void Main(string[] args)
{
    Holder holder = new Holder(0);
    Holder holder2 = new Holder(0);
    Test A = new Test(holder);
    Test B = new Test(holder);
    Test C = new Test(holder2);
    A.write(); //Writes 1 should write 1
    B.write(); //Writes 2 should write 2
    C.write(); //Writes 1 should write 1
    Console.ReadLine();
}
class Holder
{
    public Holder(int i)
    {
        num = i;
    }
    public int num;
}
class Test
{
    Holder _holder;
    public Test(Holder holder)
    {
        _holder = holder;
    }
    public void write()
    {
        var b = System.Threading.Interlocked.Increment(ref _holder.num);
        Console.WriteLine(b);
    }
}

Is there a better way than this?

+1  A: 

It is not possible to store a reference as a field.

You need to hold the int in a class.

SLaks
Does that mean the way my second example is is the "Best-Practices" way of doing it?
Scott Chamberlain
If you need to do it, yes, or you can switch to unsafe code and lobby pointers around.
Lasse V. Karlsen
@Lase: but you would have to 'pin' the targets. Not very practical.
Henk Holterman
+4  A: 

Basically, the answer is Yes, you need a class.

There is no concept of 'reference to int' that you can store as a field. In C# it is limited to parameters.

And while there is an unsafe way (pointer to int, int*) the complexities of dealing with the GC in that scenario make it impractical and inefficient.

So your second example looks OK.

Henk Holterman
+2  A: 

You cannot store a reference to a variable, for precisely the reason that someone could do what you are doing: take a reference to a local variable, and then use that reference after the local variable's storage is reclaimed.

Your approach of making the variable into a field of a class is fine. An alternative way of doing the same thing is to make getter and setter delegates to the variable. If the delegates are closed over an outer local variable, that outer local will be hoisted to a field so that its lifetime is longer than that of the delegates.

Eric Lippert