代码之家  ›  专栏  ›  技术社区  ›  Paul Turner

variable=null作为“对象销毁”从何而来?

  •  22
  • Paul Turner  · 技术社区  · 14 年前

    在许多不同的公司中,我对许多用.NET不同版本编写的遗留系统进行了研究,不断发现以下模式的示例:

    public void FooBar()
    {
        object foo = null;
        object bar = null;
    
        try
        {
           foo = new object();
           bar = new object();
    
           // Code which throws exception.
        }
        finally
        {
           // Destroying objects
           foo = null;
           bar = null;
        }
    
    }
    

    对于任何知道.NET内存管理工作原理的人来说,这种代码是完全没有必要的;垃圾收集器不需要您手动分配 null 无效的

    那么,为什么我一直在寻找这种模式呢?有没有学校教这种做法?有没有一种语言 无效的 要正确管理内存,需要本地作用域变量的值吗?显式赋值是否有附加值 无效的 我还没察觉到?

    14 回复  |  直到 14 年前
        1
  •  22
  •   Community kfsone    7 年前

    它是 福德 cargo cult programming (感谢 Daniel Earwicker )那些习惯于“释放”资源、糟糕的GC实现和糟糕的API的开发人员。

    一些gc不能很好地处理循环引用。为了摆脱它们,你必须打破“某处”的循环。在哪里?好吧,如果有疑问,那就到处都是。这样做一年,它就会进入你的指尖。

    null 给你“做某事”的想法,因为作为开发者,我们总是害怕“忘记某事”。

    最后,我们有必须显式关闭的api,因为没有真正的语言支持说“当我完成它时关闭它”,让计算机像GC一样解决它。所以你有一个必须调用清理代码的API和一个不需要的API。这很糟糕,而且鼓励像上面这样的模式。

        2
  •  8
  •   Brian Gideon    14 年前

    Nothing (相当于null)将减少引用计数。一旦该计数变为零,则对象将同步销毁。在离开方法的作用域时,计数将自动递减,因此即使在VB中,这种技术也基本上是无用的,但是在某些特殊情况下,您可能希望贪婪地销毁一个对象,如下面的代码所示。

    Public Sub Main()
      Dim big As Variant
      Set big = GetReallyBigObject()
      Call big.DoSomething
      Set big = Nothing
      Call TimeConsumingOperation
      Call ConsumeMoreMemory
    End Sub
    

    big 会一直徘徊到最后没有召唤 Set big = Nothing . 如果方法中的其他内容是耗时的操作或产生更多内存压力,那么这可能是不需要的。

        3
  •  3
  •   Justin Niessner    14 年前

    它来自C/C++,其中明确地将指针设置为空是标准(消除)。 dangling pointers )

    调用free()后:

    #include <stdlib.h>
    {
        char *dp = malloc ( A_CONST );
    
        // Now that we're freeing dp, it is a dangling pointer because it's pointing
        // to freed memory
        free ( dp );
    
        // Set dp to NULL so it is no longer dangling
        dp = NULL;
    }
    

        4
  •  3
  •   Philipp    14 年前

    它在具有确定性垃圾收集且没有RAII的语言中更为常见,例如旧的Visual Basic, 但即使这样也没必要 在那里经常需要打破循环引用。这可能是因为那些在地方使用哑指针的坏C++程序员。在C++中,删除DUB指针后,将其设置为0是有意义的,以防止双重删除。

        5
  •  2
  •   Tim Coker    14 年前

    我在VBScript代码(经典的ASP)中看到过很多这样的东西,我想它就是从那里来的。

        6
  •  2
  •   Grzenio    14 年前

        7
  •  2
  •   Eric Lippert    14 年前

    我猜想这种模式来自于将C++代码翻译成C语言,而不停顿以理解C语言终结和C++最终化之间的差异。在C++中,我经常在析构函数中排除一些东西,或者用于调试目的(这样,您可以在调试器中看到引用不再有效),或者很少,因为我希望释放一个智能对象。如果这是我想要的意思 呼叫释放

    由于不同的原因,在VB/VBScript中也经常看到这种模式。我想了想是什么导致了这种情况:

    http://blogs.msdn.com/b/ericlippert/archive/2004/04/28/122259.aspx

        8
  •  1
  •   Amarghosh    14 年前

    可归零的约定起源于 foo

        9
  •  1
  •   Morfildur    14 年前

    它来自C/C++,在一个已经释放的指针上做一个For()/删除可能导致崩溃,而释放一个空指针根本不起作用。

    这意味着这个构造(C++)会引起问题。

    void foo()
    {
      myclass *mc = new myclass(); // lets assume you really need new here
      if (foo == bar)
      {
        delete mc;
      }
      delete mc;
    }
    

    void foo()
    {
      myclass *mc = new myclass(); // lets assume you really need new here
      if (foo == bar)
      {
        delete mc;
        mc = NULL;
      }
      delete mc;
    }
    

    结论:在C#、Java和其他垃圾收集语言中,这是完全不必要的。

        10
  •  1
  •   John R. Strohm    5 年前

    考虑一个小小的修改:

    public void FooBar() 
    { 
        object foo = null; 
        object bar = null; 
    
        try 
        { 
           foo = new object(); 
           bar = new object(); 
    
           // Code which throws exception. 
        } 
        finally 
        { 
           // Destroying objects 
           foo = null; 
           bar = null; 
        } 
        vavoom(foo,bar);
    } 
    

    作者可能希望确保great-Vavoom(*)不会在以前抛出并捕获异常时获取指向错误对象的指针。偏执,导致防御性编码,不一定是一件坏事在这个行业。

        11
  •  0
  •   Chris    14 年前

        12
  •  0
  •   ChrisF toni    14 年前

    我可以从对垃圾收集工作原理的误解,或者迫使GC立即启动的努力中看到它——也许是因为 foo bar 它们相当大。

        13
  •  0
  •   Jonathan Sternberg    14 年前

    我以前在一些Java代码中见过这种情况。它被用在一个静态变量上,表示应该销毁对象。

    不过,它可能不是源于Java,因为将它用于除静态变量以外的任何其他对象在Java中也没有意义。

        14
  •  -4
  •   Wernight    14 年前

    它来自C++代码,尤其是C++代码。 . 在这种情况下,它就相当于 .Dispose() 在C#。

    null 在C#,except可能帮助GC打破循环引用。