-1

I have a small issue I wanted to clear in my head. I have a integer that I increment every second. I pass this integer as a reference to another form and want to display it. So through a button click I instanciate the second form and point the reference towards a local integer in my second form.

I display the value every second on the second form but it will only update when I recreate a new form2 instance.

public partial class Form1 : Form
    {
        private static int test = 0;
        public Form1()
        {
            InitializeComponent();
            TestClass.Init();

            Timer t = new Timer();
            t.Interval = 1000;
            t.Tick += new EventHandler(tick);
            t.Start();
        }

        private void tick(object sender, EventArgs e)
        {
            ++test;
        }

        public delegate void TestEventHandler(ref int test);
        public static event TestEventHandler TestEvent;

        internal static void TestEventRaised(ref int test)
        {
            TestEvent?.Invoke(ref test);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            TestEventRaised(ref test);
        }
    }
    public static class TestClass
    {
        private static Form2 form2;
        public static void Init()
        {
            Form1.TestEvent += new Form1.TestEventHandler(Event);
        }

        private static void Event(ref int test)
        {
            if (form2 != null)
            {
                form2.Close();
                form2 = null;
            }
            form2 = new Form2(ref test);
            form2.ShowDialog();
        }
    }
    public partial class Form2 : Form
    {
        int _test = 0;
        public Form2(ref int test)
        {
            InitializeComponent();
            _test = test;

            Timer t = new Timer();
            t.Interval = 1000;
            t.Tick += new EventHandler(tick);
            t.Start();
        }

        private void tick(object sender, EventArgs e)
        {
            label1.Text = _test.ToString();
        }
    }

I do not understand why this is not working since when the constructor of form2 is called, I link _test to test. TestClass has its purpose in my "real" code which is to link both Form1 and Form2 that are DLLs.

Do you have any idea why this is not working ?

Thanks !

Nicola
  • 91
  • 8
  • 1
    Word of advice: separate your data from your presentation. Move that integer and timer into a separately managed class which will perform the increments or whatever. Your life will be much simpler. – Tanveer Badar Aug 26 '20 at 15:26
  • 1
    @TanveerBadar It is in my "real" code. However, for the sake of simplicity and not add hundreds of lines of code in this question, I merged it all together. – Nicola Aug 26 '20 at 15:32

1 Answers1

2

when the constructor of form2 is called, I link _test to test

No, you don't.

This line of code in the Form2 constructor:

_test = test;

... copies the current value of the test parameter to the _test field. That's all it does. The fact that your test parameter is a ref parameter is irrelevant in this case, because you never write to it in the constructor (and nothing updates that parameter in another thread either, which would be visible).

Instead of having two separate int fields, I suggest you have a Counter class instead:

public class Counter
{
    // TODO: Potentially thread safety, or consider making Value publicly read-only
    // with an "Increment" method
    public int Value { get; set; }
}

Then both Form1 and Form2 can have a reference to the same Counter object, at which point changes in the content of that object will be visible from both forms. (I'd also suggest avoiding static fields for this.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I like this idea that i've already used at several other places in my work. However, the drawback of this would be to create a new dependency with 3 lines of code in it for this only usage. The other solution would be to add this Counter class in one of Form1 or Form2 dependency and the other one calling it, but it will create interdependencies that I would like to avoid to keep the whole maintainable. – Nicola Aug 26 '20 at 15:36
  • Is there no way to keep a link using the ref, such as a pointer will do in C++ ? – Nicola Aug 26 '20 at 15:37
  • @Nicola: No, there isn't. An `int` field will *always* be independent of other fields. It's possible that in a future version of C# you could have a `ref` field within a `ref struct`, but I don't believe that's the case now, and it wouldn't help you anyway given that the forms are classes. You need a reference type so that the two forms can reference the same data. You could make this generic - a `Wrapper` class that just has a simple `public T Value { get; set; }` if you don't care about any encapsulation. Or use a single-element array if you *really* don't want your own type. – Jon Skeet Aug 26 '20 at 15:39
  • Thank you for your reply. I managed to make it work by replacing the integer with an array of integers (size = 1). I imagine that _test = test would just point to the right data in this case. – Nicola Aug 26 '20 at 15:45
  • 1
    @Nicola: Yes, because arrays are always reference types - but I would *strongly* encourage you to introduce a new class instead. Sure, that's a dependency - but it's much less of an ugly hack than a single-element array IMO. – Jon Skeet Aug 26 '20 at 15:54