请注意以下代码:
Control foo = null;
Control bar = null;
int i = 0;
protected void Page_Load(object sender, EventArgs e)
{
test();
test();
Page.Controls.Add(foo);
}
void test()
{
i++;
bar = new Control();
bar.Controls.Add(new LiteralControl(i.ToString()));
if (foo == null)
{
foo = new Control();
foo.Controls.Add(bar);
}
}
在尝试上面的代码时,我很惊讶地看到打印的结果为'1'(不是'2')。
我假设这是因为当我将控件bar
添加到foo
时,foo.Controls.Add()
会解析引用bar
,而不是仅存储引用本身。
1)任何人都可以确认是这种情况,还是可能会详细说明?
2)我有一种感觉,如果我被允许foo.Controls.Add(ref bar);
它会显示'2',但显然语法是非法的。如果没有重大的重构,是否可能出现这种情况?
答案 0 :(得分:6)
致电
foo.Controls.Add(bar);
使用当前值bar
。这是指Control
包含LiteralControl
,文字为“1”。
现在稍后,您更改bar
的值以引用完全不同的控件...但这对foo.Controls
中已存储的内容没有任何影响。即使您可以通过引用将bar
传递给Add
方法,也不会产生任何影响。
我强烈建议如果您不确定引用,参数等在.NET中是如何工作的,那么您使用控制台应用程序来检查您的理解。他们很多更容易玩,调试等。
您可能还想阅读parameter passing和value/reference types上的文章。
答案 1 :(得分:3)
比这简单得多。 foo是类范围的对象。第二次运行test()时,没有任何反应,因为它不是null。所以它只是从第一个test()返回你添加的内容,这是一个新的LiteralControl,字符串值为“1”。 “1”只是文本,它不是对i的引用。
顺便说一句foo.Controls.Add(ref bar)
实际上发生了什么。控件(对象)总是通过引用添加,这是它们存在的本质。要将对象的实际值传递给某些东西,您基本上必须首先复制该对象。但你仍然只是传递对副本的引用。
如果您更改了bar
的内容,它确实会更改输出。
我认为你在这里感到困惑的是你第二次运行test()时没有处理相同的bar
,因为你每次都会创建一个新的。{/ p>
(编辑)
顺便说一句,如果你改变了这一行:
bar = new Control();
到
if (bar == null) bar = new Control();
它会按预期运行,因为第二次通过你不会抛出原来的“bar”引用,而是像现在一样用新的替换它。 (Sorta ...实际上它会说“12”,因为你已经添加了两个LiteralControls。)
答案 2 :(得分:3)
引用类型的变量(例如bar和foo)不包含对象,它们包含对象的引用。
因此,第一次通过test()方法时,bar包含对Control的引用,其中包含1。并且该引用被添加到foo。
第二次通过test()方法,bar包含对不同控件的引用,其中包含2。并且该引用不会被添加到foo。
为什么它第二次出现不同的参考?因为在test()开始时你说bar = new Control();
它存储了对新控件的引用。从那时起,bar与之前引用的Control无关,尽管在foo的Controls集合中仍然存在对该控件的引用。
如果您想更改代码以使其符合预期,您可以这样做:
Control foo = null;
Control bar = new Control();
int i = 0;
protected void Page_Load(object sender, EventArgs e)
{
test();
test();
Page.Controls.Add(foo);
}
void test()
{
i++;
bar.Controls.Clear()
bar.Controls.Add(new LiteralControl(i.ToString()));
if (foo == null)
{
foo = new Control();
foo.Controls.Add(bar);
}
}
在这种情况下,我们只在bar中存储一个新的引用,所以当完成所有操作后,它仍然应该与我们添加到foo的引用相匹配。
答案 3 :(得分:0)
第一次test()
运行时,会创建一个新控件(我将其称为Control1
),并将其引用存储在bar
中。因为foo
为null,所以它被设置为一个新控件,添加了bar中的引用。
现在Control1
现在bar
和foo.Controls集合都引用了test()
。
第二次Control2
运行时,会创建一个新控件(foo
),a和存储在bar中的引用。 bar
现在不为空,因此不会被修改。
所以现在Control2
是对foo.Controls
的引用,但Control1
仍然包含对{{1}}的引用。
答案 4 :(得分:0)
好的foo只创建一次(在第一次调用测试时),然后添加标签为1的LiteralControl。
在第二次调用test()时,旧引用(标签1)将替换为新控件。但是,添加到foo的栏仍然存在且不受影响。