What your code is doing
- Create "test" object null
You actually aren't creating an object. You are setting test
to a null reference.
- Pass the object as reference to the form
You are actually passing a reference to a reference to test
. If you just want to pass a reference, remove the ref
keyword. The value of a variable that is a reference type is already just reference to the object. A ref object is not a reference to an object but a reference to an object reference. See this link if this is not clear to you.
- Form construct takes the "test" reference and assign it to object "cadena"
You are populating the variable cadena
with a null.
3a. (since they are objects and are passed by reference I would guess that Cadena now has the address of test)
No. It has the value of test
, which is a null reference. I think you are trying to store a reference to an object reference (i.e. a pointer to test
). You are in fact only storing a pointer that is equivalent to the value held in test
, which in this case is null.
- When the button in form 1 is pressed the constructor is called and the textbox string is assigned, the form is closed.
Correct
- Use a messagebox to check the string in the test object (which should have the string assigned in form 1 but all we have is a null reference.
There is no code in your example which goes back and populates test
with a valid reference.
Why this will never work
If you want to pass a pointer to a variable, you will need to use unmanaged code, according to this article:
In an unsafe context, a type may be a pointer type, a value type, or a reference type. A pointer type declaration takes one of the following forms:
(emphasis added)
You can't do it in c#... and in c++ I wouldn't do it, either, since there is no guarantee the pointer will get garbage collected. That's why this pattern is not allowed in managed code.
How to do what you're trying to do
I'll give you three options. There are probably a few more (Singleton, ByRef<T>, etc.) but I think these are the most common.
Set up parent/child between program and form(s)
If you are writing an app where you have a series of forms that need to access global state, one common pattern is to make the Program
itself the container for global state, and pass it as a parent
parameter to anything that needs it. So here's your code again, following this pattern:
public class String_Example
{
public string _str {get;set;}
public String_Example(string Cadena)
{
_str = Cadena;
}
}
public partial class Form1 : Form
{
readonly Program _parent; //Reference to class which contains global state. readonly = can only be set in constructor and can never change.
public Form1(Program parent)
{
InitializeComponent();
_parent = parent;
}
private void button1_Click(object sender, EventArgs e)
{
_parent.test = new String_Example(textBox1.Text);
this.Close();
}
}
public class Program
{
public String_Example test; //Notice this is now a member variable
static public void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
test = null;
Form1 frm = new Form1(this);
Application.Run(frm);
MessageBox.Show(test._str);
}
}
Make the global variables static
Or, if you don't want to pass pointers around to child objects, you can make the global variables static
:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Program.test = new String_Example(textBox1.Text);
this.Close();
}
}
public class Program
{
static public String_Example test; //Notice this is now a static variable, so it can be accessed without a reference to an instance of Program
static public void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
test = null;
Form1 frm = new Form1(this);
Application.Run(frm);
MessageBox.Show(test._str);
}
}
The "best" way
The best way, or the most modern way that seems to be trendy these days, is to
- Create a class (or classes) specifically intended to contain global state
- Implement the state variables as member variables
- Use a factory like Autofac to get an instance of the state container(s)
- Tell the factory you only want a single instance no matter how many times you call it
For example
class ProgramGlobals
{
public String_Example Test {get; set; }
}
class Program
{
static void SetupFactory()
{
var builder = new ContainerBuilder();
builder.RegisterType<ProgramGlobals>().SingleInstance();
}
static void Main()
{
SetUpFactory();
var globals = Container.Resolve<ProgramGlobals>();
globals.Test = null;
Form1 frm = new Form1();
Application.Run(frm);
MessageBox.Show(globals.Test._str);
}
}
public partial class Form1: Form
{
private voide button1_Click(object sender, EventArgs e)
{
var globals = Container.Resolve<ProgramGlobals>();
globals.Test._str = new String_Example(textbox1.Text)
}
}
You are free to use whatever IoC toolkit you want, e.g. AutoFac or Unity. This article compares the major IoC libraries that are available for .NET.
This method is preferred because it allows a unit tester to provide their own stub for global state without mocking, which is often the hardest part of setting up unit tests.
Note that in the example above I am getting references to ProgramGlobals with executing code. You can also set up your objects to get a reference automatically, using Dependency Injection. Great topic, but my answer is already pretty long, so you may have to research that on your own.