Reputation: 77
I want to create a global instance and don't know if its possible, take the following for example
I have a String_Example Class:
namespace Pass_Object_as_Reference_Example
{
public class String_Example
{
public string _str {get;set;}
public String_Example(string Cadena)
{
_str = Cadena;
}
}
}
And this is my Form1 code:
public partial class Form1 : Form
{
String_Example cadena;
public Form1(ref String_Example str)
{
InitializeComponent();
cadena = str;
}
private void button1_Click(object sender, EventArgs e)
{
cadena = new String_Example(textBox1.Text);
this.Close();
}
}
This is my Main code:
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
String_Example test = null;
Form1 frm = new Form1(ref test);
Application.Run(frm);
MessageBox.Show(test._str);
}
This is what I imagine would happen:
I think that when I'm calling the constructor I'm destroying the first reference and that is why the final object is null
right?
Should I create the object in the main code and just modify the parameters in the subsequent forms?
Upvotes: 0
Views: 5019
Reputation: 52240
You actually aren't creating an object. You are setting test
to a null reference.
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.
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.
Correct
There is no code in your example which goes back and populates test
with a valid reference.
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.
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
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.
Upvotes: 2
Reputation: 10401
The simplest way to achieve what you want without using global state is to use some class like:
public class ByRef<T>
{
public T Value
{
get;
set;
}
}
Then in Main
:
var byRef = new ByRef<String_Example>();
Form1 frm = new Form1(byRef);
And in Form1
:
public partial class Form1 : Form
{
ByRef<String_Example> cadena;
public Form1(ByRef<String_Example> str)
{
if (str == null)
throw new ArgumentNullException("str");
InitializeComponent();
cadena = str;
}
private void button1_Click(object sender, EventArgs e)
{
cadena.Value = new String_Example(textBox1.Text);
this.Close();
}
}
Upvotes: 0