Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving a reference to a int

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?

like image 833
Scott Chamberlain Avatar asked May 30 '10 19:05

Scott Chamberlain


2 Answers

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.

like image 83
Henk Holterman Avatar answered Sep 21 '22 05:09

Henk Holterman


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.

like image 37
Eric Lippert Avatar answered Sep 25 '22 05:09

Eric Lippert