Question C# Pass object by reference - uninitialized

Zatnikitelman

Addon Developer
Addon Developer
Joined
Jan 13, 2008
Messages
2,302
Reaction score
6
Points
38
Location
Atlanta, GA, USA, North America
I'm working on a C# program outside of Orbiter, and I've hit a little snag. I need to pass a dictionary from one class (a windows form) into another (call it a child form, though there's no polymorphic relationship) with the result being that I'm able to edit the original dictionary. In C++ this is a no brainer, just pass a pointer to the object to the class, but I'm obviously missing something with C#. Here's my setup, first is the parent class.
Code:
public partial class Generator : Form
    {
        private Dictionary<uint, Block> dictBlocks;

        public Generator()
        {
            InitializeComponent();
            dictBlocks = new Dictionary<uint, Block>();
        }
...
        private void btnBlockAdd_Click(object sender, EventArgs e)
        {
            BlockProperties BlockPropertyDiag = new BlockProperties(ref dictBlocks);
            new BlockProperties().ShowDialog();
        }
}
Now the windows form I'm opening:
Code:
public partial class BlockProperties : Form
    {
        private Dictionary<uint, Block> dictBlocks;

        public BlockProperties(ref Dictionary<uint, Block> dictPassBlocks)
        {
            InitializeComponent();
            this.dictBlocks = dictPassBlocks;
        }
...
        private void btnDone_Click(object sender, EventArgs e)
        {
            uint iBlockNum;
            if(uint.TryParse(txtBlockNum.Text, out iBlockNum))
            {
                Block b = new Block();
                dictBlocks.Add(iBlockNum, b);
                this.Close();
            }
            else
            {
                MessageBox.Show("Invalid Block Number", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0, false);
                txtBlockNum.Clear();
            }
            
        }
    }
The problem is that when I click the Done button on the child form, dictBlocks is back to null. Does anyone know how to handle this? I need the child form to send data back to the parent since the child form just sort of serves as a way to conveniently enter the data.

Thanks for any help!

Matt
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,882
Reaction score
2,133
Points
203
Location
between the planets
In C++ this is a no brainer, just pass a pointer to the object to the class, but I'm obviously missing something with C#.

Indeed you are. In C#, as in Java, everything's a reference. Even primitives like int and float are packed into references (EDIT: In C#, not in Java), although the language invokes special passing conventions for those, so they behave like they're passed by value. Which is about the only use for the ref keyword there is. I'm working in C# for over a year now, and I didn't even know the keyword existed until I saw it in your code and looked it up. I.e. what you're trying to do is what happens by default.
 
Last edited:

Urwumpe

Not funny anymore
Addon Developer
Donator
Joined
Feb 6, 2008
Messages
37,627
Reaction score
2,345
Points
203
Location
Wolfsburg
Preferred Pronouns
Sire
Indeed you are. In C#, as in Java, everything's a reference. Even primitives like int and float are packed into references, although the language invokes special passing conventions for those, so they ehave like they're passed by value.

Actually, if I remember the VM specification correctly, autoboxing is not used in Java for passing ints and floats to functions. But it is used if you are using generics.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,882
Reaction score
2,133
Points
203
Location
between the planets
Actually, if I remember the VM specification correctly, autoboxing is not used in Java for passing ints and floats to functions.

Sorry, a bit misunderstandable there. Yes, Java preserves true primitives and provides wrappers for them for use in generics. I was exclusively talking about C# here, where you only have the wrappers. Only they're named in a way that it takes a while to notice.
 

Urwumpe

Not funny anymore
Addon Developer
Donator
Joined
Feb 6, 2008
Messages
37,627
Reaction score
2,345
Points
203
Location
Wolfsburg
Preferred Pronouns
Sire
Sorry, a bit misunderstandable there. Yes, Java preserves true primitives and provides wrappers for them for use in generics. I was exclusively talking about C# here, where you only have the wrappers. Only they're named in a way that it takes a while to notice.

Well, even C# employs a JIT at a point, so all is x86 code and datatypes eventually. :lol:
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,882
Reaction score
2,133
Points
203
Location
between the planets
Spaghetti assembler code we are, and to spaghetti assembler code we shall return! :lol:

---------- Post added at 06:54 PM ---------- Previous post was at 06:46 PM ----------

@Zatnikitelman:

It does indeed appear that the ref keyword is your problem here. I just did some reading on MSDN, and this is what they say about the matter:

Passing a reference type by reference enables the called method to replace the object in the calling method to which the reference parameter refers. The storage location of the object is passed to the method as the value of the reference parameter.

I.e. it would appear that it behaves like **, if I understood that sentence correctly. Which would be the logically expected behavior, but then you don't want to trust in expected behavior too much when working with C#.
 

Zatnikitelman

Addon Developer
Addon Developer
Joined
Jan 13, 2008
Messages
2,302
Reaction score
6
Points
38
Location
Atlanta, GA, USA, North America
Ok, so it sounds like my code should work then. So why is it still showing as null when I call the later method? The object isn't destroyed at any point after construction.
 

Urwumpe

Not funny anymore
Addon Developer
Donator
Joined
Feb 6, 2008
Messages
37,627
Reaction score
2,345
Points
203
Location
Wolfsburg
Preferred Pronouns
Sire
Ok, so it sounds like my code should work then. So why is it still showing as null when I call the later method? The object isn't destroyed at any point after construction.

Not the object, but the initial reference. I think of it like that:

ref dictPassBlocks --> ref dictBlocks --> dictBlocks --> Dictionary

The "ref dictBlocks" above exists only btnBlockAdd_Click.

Correct me if I am wrong. Java does not know such nonsense like a "ref" keyword.
 

Blake

Addon Developer
Addon Developer
Joined
Mar 9, 2009
Messages
235
Reaction score
123
Points
58
Location
Lehi, Utah
Code:
        private void btnBlockAdd_Click(object sender, EventArgs e)
        {
            BlockProperties BlockPropertyDiag = new BlockProperties(ref dictBlocks);
            new BlockProperties().ShowDialog();
        }

Looks like you are 'new'ing' a new dialog and calling it, rather then the 'BlockPropertyDiag' you created. Try this. And yes, lose the 'ref' keyword here and in the constructor.

Code:
        private void btnBlockAdd_Click(object sender, EventArgs e)
        {
            using (var diag = new BlockProperties(dictBlocks))
           {
                diag.ShowDialog();
           }
        }
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
:hesaid:

Code:
        private void btnBlockAdd_Click(object sender, EventArgs e)
        {
            BlockProperties BlockPropertyDiag = new BlockProperties(ref dictBlocks);
            new BlockProperties().ShowDialog();
        }

You are creating 2 objects there. The first line uses the constructor with the dict as argument, but never shows this object as dialog. The second line uses the default constructor (perhaps from the autogenerated code that is missing in your post... the "partial" keyword is a hint here) and shows the second object as a dialog. However, in this second object you never passed in the dictionary, so the dict is null. I guess you get a NullReferenceException.

Try this instead:
Code:
        private void btnBlockAdd_Click(object sender, EventArgs e)
        {
            BlockProperties BlockPropertyDiag = new BlockProperties(ref dictBlocks);
            BlockPropertyDiag.ShowDialog();
        }
And yes, "ref" is often a code smell.
 

Zatnikitelman

Addon Developer
Addon Developer
Joined
Jan 13, 2008
Messages
2,302
Reaction score
6
Points
38
Location
Atlanta, GA, USA, North America
:facepalm: That's what I get for late-night coding sessions. I enjoy them, but man do they have some downsides. Yea, the issue seems to be the typo of still newing it, which I didn't catch because my object is named similarly to my class.

Thanks Blake and Face for saving me, and thanks everyone for the interesting discussion. I learned some new things too!
 
Top