基础.NET - 如何“存储/解析”引用

时间:2010-10-28 16:17:49

标签: c# asp.net pointers reference

请注意以下代码:

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',但显然语法是非法的。如果没有重大的重构,是否可能出现这种情况?

5 个答案:

答案 0 :(得分:6)

致电

foo.Controls.Add(bar);

使用当前值bar 。这是指Control包含LiteralControl,文字为“1”。

现在稍后,您更改bar的值以引用完全不同的控件...但这对foo.Controls中已存储的内容没有任何影响。即使您可以通过引用将bar传递给Add方法,也不会产生任何影响。

强烈建议如果您不确定引用,参数等在.NET中是如何工作的,那么您使用控制台应用程序来检查您的理解。他们很多更容易玩,调试等。

您可能还想阅读parameter passingvalue/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)

Jon Skeet的答案很好(当然),但我只想强调一点:

引用类型的变量(例如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的栏仍然存在且不受影响。