全局对象实例?
Global object instance?
我想创建一个全局实例,不知道是否可行,以下面为例
我有一个 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;
}
}
}
这是我的 Form1 代码:
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();
}
}
这是我的主要代码:
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);
}
这是我想象中会发生的事情:
- 创建 "test" 对象 null
- 将对象作为对表单的引用传递
- Form 构造采用 "test" 引用并将其分配给对象 "cadena"(因为它们是对象并且通过引用传递,我猜 Cadena 现在有 test 的地址)
- 当按下表单 1 中的按钮时,调用构造函数并分配文本框字符串,关闭表单
- 使用消息框检查测试对象中的字符串(应该在表单 1 中分配字符串,但我们只有空引用。
我认为当我调用构造函数时我正在破坏第一个引用,这就是为什么最终对象是 null
对吗?
是否应该在主代码中创建对象,在后面的表单中只修改参数?
您的代码在做什么
- 创建 "test" 对象 null
您实际上并不是在创建对象。您正在将 test
设置为空引用。
- 将对象作为对表单的引用传递
您实际上是在传递对 test
的引用。如果您只想传递引用,请删除 ref
关键字。引用类型的变量的值已经只是对该对象的引用。 ref 对象不是对对象的引用,而是对对象引用的引用。如果您不清楚,请参阅 this link。
- Form 构造采用 "test" 引用并将其分配给对象 "cadena"
您正在用空值填充变量 cadena
。
3a. (因为它们是对象并且通过引用传递我猜想 Cadena 现在有测试地址)
没有。它的值为 test
,这是一个空引用。我认为您正在尝试存储对对象引用的引用(即指向 test
的指针)。实际上,您只存储了一个指针,该指针等于 test
中保存的值,在本例中为 null。
- 当按下表单 1 中的按钮时,调用构造函数并分配文本框字符串,关闭表单。
正确
- 使用消息框检查测试对象中的字符串(应该在表单 1 中分配字符串,但我们只有一个空引用。
您的示例中没有返回并使用有效引用填充 test
的代码。
为什么这永远行不通
根据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:
(强调)
你不能在 c# 中这样做...在 c++ 中我也不会这样做,因为不能保证指针会被垃圾收集。这就是托管代码中不允许使用此模式的原因。
如何做您想做的事
我给你三个选择。可能还有更多(Singleton、ByRef 等),但我认为这些是最常见的。
在程序和表单之间设置parent/child
如果您正在编写一个应用程序,其中有一系列需要访问全局状态的表单,一个常见的模式是使 Program
本身成为全局状态的容器,并将其作为 parent
参数传递给任何需要它的东西。所以这里又是你的代码,遵循这种模式:
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);
}
}
将全局变量设为静态
或者,如果您不想将指针传递给子对象,您可以使全局变量 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);
}
}
"best"方式
最好的方式,或者说最近流行的最现代的方式,就是
- 创建一个专门用于包含全局状态的 class(或 classes)
- 将状态变量实现为成员变量
- 使用像 Autofac 这样的工厂来获取状态容器的实例
- 告诉工厂无论调用多少次都只需要一个实例
例如
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)
}
}
您可以自由使用 IoC toolkit you want, e.g. AutoFac or Unity. This article 比较可用于 .NET 的主要 IoC 库。
首选此方法,因为它允许单元测试人员为全局状态提供自己的存根,而无需 ,这通常是设置单元测试中最困难的部分。
请注意,在上面的示例中,我通过执行代码获取了对 ProgramGlobals 的引用。您还可以使用 Dependency Injection 将对象设置为自动获取引用。很好的话题,但是我的回答已经很长了,所以你可能需要自己研究一下。
在不使用 global state 的情况下实现您想要的效果的最简单方法是使用一些 class,例如:
public class ByRef<T>
{
public T Value
{
get;
set;
}
}
然后在Main
:
var byRef = new ByRef<String_Example>();
Form1 frm = new Form1(byRef);
在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();
}
}
我想创建一个全局实例,不知道是否可行,以下面为例
我有一个 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;
}
}
}
这是我的 Form1 代码:
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();
}
}
这是我的主要代码:
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);
}
这是我想象中会发生的事情:
- 创建 "test" 对象 null
- 将对象作为对表单的引用传递
- Form 构造采用 "test" 引用并将其分配给对象 "cadena"(因为它们是对象并且通过引用传递,我猜 Cadena 现在有 test 的地址)
- 当按下表单 1 中的按钮时,调用构造函数并分配文本框字符串,关闭表单
- 使用消息框检查测试对象中的字符串(应该在表单 1 中分配字符串,但我们只有空引用。
我认为当我调用构造函数时我正在破坏第一个引用,这就是为什么最终对象是 null
对吗?
是否应该在主代码中创建对象,在后面的表单中只修改参数?
您的代码在做什么
- 创建 "test" 对象 null
您实际上并不是在创建对象。您正在将 test
设置为空引用。
- 将对象作为对表单的引用传递
您实际上是在传递对 test
的引用。如果您只想传递引用,请删除 ref
关键字。引用类型的变量的值已经只是对该对象的引用。 ref 对象不是对对象的引用,而是对对象引用的引用。如果您不清楚,请参阅 this link。
- Form 构造采用 "test" 引用并将其分配给对象 "cadena"
您正在用空值填充变量 cadena
。
3a. (因为它们是对象并且通过引用传递我猜想 Cadena 现在有测试地址)
没有。它的值为 test
,这是一个空引用。我认为您正在尝试存储对对象引用的引用(即指向 test
的指针)。实际上,您只存储了一个指针,该指针等于 test
中保存的值,在本例中为 null。
- 当按下表单 1 中的按钮时,调用构造函数并分配文本框字符串,关闭表单。
正确
- 使用消息框检查测试对象中的字符串(应该在表单 1 中分配字符串,但我们只有一个空引用。
您的示例中没有返回并使用有效引用填充 test
的代码。
为什么这永远行不通
根据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:
(强调)
你不能在 c# 中这样做...在 c++ 中我也不会这样做,因为不能保证指针会被垃圾收集。这就是托管代码中不允许使用此模式的原因。
如何做您想做的事
我给你三个选择。可能还有更多(Singleton、ByRef
在程序和表单之间设置parent/child
如果您正在编写一个应用程序,其中有一系列需要访问全局状态的表单,一个常见的模式是使 Program
本身成为全局状态的容器,并将其作为 parent
参数传递给任何需要它的东西。所以这里又是你的代码,遵循这种模式:
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);
}
}
将全局变量设为静态
或者,如果您不想将指针传递给子对象,您可以使全局变量 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);
}
}
"best"方式
最好的方式,或者说最近流行的最现代的方式,就是
- 创建一个专门用于包含全局状态的 class(或 classes)
- 将状态变量实现为成员变量
- 使用像 Autofac 这样的工厂来获取状态容器的实例
- 告诉工厂无论调用多少次都只需要一个实例
例如
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)
}
}
您可以自由使用 IoC toolkit you want, e.g. AutoFac or Unity. This article 比较可用于 .NET 的主要 IoC 库。
首选此方法,因为它允许单元测试人员为全局状态提供自己的存根,而无需
请注意,在上面的示例中,我通过执行代码获取了对 ProgramGlobals 的引用。您还可以使用 Dependency Injection 将对象设置为自动获取引用。很好的话题,但是我的回答已经很长了,所以你可能需要自己研究一下。
在不使用 global state 的情况下实现您想要的效果的最简单方法是使用一些 class,例如:
public class ByRef<T>
{
public T Value
{
get;
set;
}
}
然后在Main
:
var byRef = new ByRef<String_Example>();
Form1 frm = new Form1(byRef);
在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();
}
}