【问题标题】:Fundamental .NET - How references are 'stored / resolved'Fundamental .NET - 如何“存储/解析”引用
【发布时间】:2010-10-28 16:17:49
【问题描述】:

注意以下代码:

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

【问题讨论】:

    标签: c# asp.net pointers reference


    【解决方案1】:

    来电

    foo.Controls.Add(bar);
    

    使用bar当前值。这指的是一个Control,其中包含一个带有文本“1”的LiteralControl

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

    我会强烈建议,如果您不确定引用、参数等在 .NET 中的工作方式,请使用控制台应用程序来检查您的理解。它们更易于使用、调试等。

    您可能还想阅读我在parameter passingvalue/reference types 上的文章。

    【讨论】:

      【解决方案2】:

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

      引用类型的变量(例如 bar 和 foo)不包含对象,它们包含对对象的引用。

      所以第一次通过 test() 方法时,bar 包含对 Control 的引用,其中包含 1。该引用被添加到 foo 中。

      第二次通过 test() 方法,bar 包含对 不同 控件的引用,该控件中有一个 2。并且该引用不会添加到 foo。

      为什么第二次引用不同?因为在 test() 的开头你说bar = new Control(); 存储了对 bar 中新控件的引用。从那里开始,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】:

        比这简单得多。 foo 是类范围的对象。第二次运行 test() 时,它没有任何反应,因为它不为空。所以它只是返回你从第一个 test() 中添加的内容,这是一个字符串值为“1”的新 LiteralControl。 “1”只是文本,不是对 i 的引用。

        顺便说一句,foo.Controls.Add(ref bar) 确实发生了。控件(对象)总是通过引用添加,这是它们存在的本质。要将对象的实际值传递给某物,您基本上必须先复制该对象。但是您仍然只是传递对副本的引用。

        如果你改变了bar 的内容,它确实会改变输出。

        我认为您在这里感到困惑的是,您第二次运行 test() 时处理的不是同一个 bar,因为您每次都创建一个新的。

        (编辑)

        顺便说一句,如果你只改变了这一行:

         bar = new Control();
        

        if (bar == null) bar = new Control();
        

        它会像预期的那样运行,因为第二次你不会像现在这样用一个新的引用替换原来的“bar”引用。 (排序......实际上它会说“12”,因为你已经添加了两个 LiteralControls 到它)。

        【讨论】:

          【解决方案4】:

          第一次运行test() 时,会创建一个新控件(我将其称为Control1),并将对它的引用存储在bar 中。因为foo 为空,所以它被设置为一个新控件,其中添加了 bar 中的引用。

          所以现在Control1 现在被bar 和 foo.Controls 集合引用。

          test() 第二次运行时会创建一个新控件 (Control2),并在 bar 中存储一个引用。 foo 现在不为空,因此不会被修改。

          所以现在bar 是对Control2 的引用,但foo.Controls 仍然包含对Control1 的引用。

          【讨论】:

            【解决方案5】:

            那么 foo 只会被创建一次(在第一次调用 test 时),然后添加标签为 1 的 LiteralControl。

            在第二次调用 test() 时,旧引用(标签 1)被新控件替换。但是,添加到 foo 的 bar 仍然存在并且不受影响。

            【讨论】:

              猜你喜欢
              • 2017-07-26
              • 2012-01-23
              • 2013-08-28
              • 2012-07-24
              • 2022-08-05
              • 2012-09-29
              • 1970-01-01
              • 2018-02-27
              • 1970-01-01
              相关资源
              最近更新 更多