变量 = null 作为“对象销毁”在哪里?来自?

发布于 2024-09-06 22:29:05 字数 645 浏览 7 评论 0原文

在许多不同的公司中,我在使用不同版本的 .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来告诉旧对象可以被收集,分配null也不会指示GC立即收集该对象。

这种模式只是噪音,让我们更难理解代码想要实现的目标。

那么,为什么我总是发现这种模式呢?有没有学校教这个练习?是否有一种语言需要将 null 值分配给本地范围的变量才能正确管理内存?显式分配 null 是否有一些我没有意识到的额外价值?

Working on a number of legacy systems written in various versions of .NET, across many different companies, I keep finding examples of the following pattern:

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;
    }

}

To anybody that knows how memory management works in .NET, this kind of code is painfully unnecessary; the garbage collector does not need you to manually assign null to tell that the old object can be collected, nor does assigning null instructs the GC to immediately collected the object.

This pattern is just noise, making it harder to understand what the code is trying to achieve.

Why, then, do I keep finding this pattern? Is there a school that teaches this practice? Is there a language in which assigning null values to locally scoped variables is required to correctly manage memory? Is there some additional value in explicitly assigning null that I haven't percieved?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

贩梦商人 2024-09-13 22:29:05

这是FUD货物邪教编程(感谢Daniel Earwicker)由习惯于“释放”资源、糟糕的 GC 实现和糟糕的 API 的开发人员开发。

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

另外,将该字段设置为 null 会给您“做某事”的想法,因为作为开发人员,我们总是担心“忘记某事”。

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

It's FUDcargo cult programming (thanks to Daniel Earwicker) by developers who are used to "free" resources, bad GC implementations and bad API.

Some GCs didn't cope well with circular references. To get rid of them, you had to break the cycle "somewhere". Where? Well, if in doubt, then everywhere. Do that for a year and it's moved into your fingertips.

Also setting the field to null gives you the idea of "doing something" because as developers, we always fear "to forget something".

Lastly, we have APIs which must be closed explicitly because there is no real language support to say "close this when I'm done with it" and let the computer figure it out just like with GC. So you have an API where you have to call cleanup code and API where you don't. This sucks and encourages patterns like the above.

人生百味 2024-09-13 22:29:05

它有可能来自 VB,它使用引用计数策略来进行内存管理和对象生存期。设置对 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

在上面的代码中,如果不调用 Set big = Nothingbig 引用的对象将一直停留到最后。如果方法中的其他内容是耗时的操作或产生更多的内存压力,那么这可能是不可取的。

It is possible that it came from VB which used a reference counting strategy for memory management and object lifetime. Setting a reference to Nothing (equivalent to null) would decrement the reference count. Once that count became zero then the object was destroyed synchronously. The count would be decremented automatically upon leaving the scope of a method so even in VB this technique was mostly useless, however there were special situations where you would want to greedily destroy an object as illustrated by the following code.

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

In the above code the object referenced by big would have lingered until the end without the call to Set big = Nothing. That may be undesirable if the other stuff in the method was a time consuming operation or generated more memory pressure.

浅唱々樱花落 2024-09-13 22:29:05

它在具有确定性垃圾收集且没有 RAII 的语言中更为常见,例如旧的 Visual Basic,但即使在那里它也是不必要的并且通常需要打破循环引用。因此,这可能确实源于糟糕的 C++ 程序员,他们到处使用哑指针。在C++中,删除哑指针后将其设置为0以防止重复删除是有意义的。

It is more common in languages with deterministic garbage collection and without RAII, such as the old Visual Basic, but even there it's unnecessary and there it was often necessary to break cyclic references. So possibly it really stems from bad C++ programmers who use dumb pointers all over the place. In C++, it makes sense to set dumb pointers to 0 after deleting them to prevent double deletion.

烧了回忆取暖 2024-09-13 22:29:05

我在 VBScript 代码(经典 ASP)中经常看到这种情况,我认为它来自那里。

I've seen this a lot in VBScript code (classic ASP) and I think it comes from there.

感悟人生的甜 2024-09-13 22:29:05

我认为这曾经是前 C/C++ 开发人员中的一个常见误解。他们知道 GC 会释放他们的内存,但他们并不真正了解何时以及如何释放。只需清洁它并继续:)

I think it used to be a common misunderstanding among former C/C++ developers. They knew that the GC will free their memory, but they didn't really understand when and how. Just clean it and carry on :)

黯然 2024-09-13 22:29:05

它来自 C/C++,其中明确将指针设置为 null 是常态(以消除悬挂指针)

调用 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;
}

经典 VB 开发人员在编写 COM 组件时也做了同样的事情,以防止内存泄漏。

It comes from C/C++ where explicitly made setting your pointers to null was the norm (to eliminate dangling pointers)

After calling 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;
}

Classic VB developers also did the same thing when writing their COM components to prevent memory leaks.

神魇的王 2024-09-13 22:29:05

我怀疑这种模式来自于将 C++ 代码转换为 C#,而没有停下来了解 C# 终结和 C++ 终结之间的差异。在 C++ 中,我经常在析构函数中将某些内容清空,或者出于调试目的(以便您可以在调试器中看到引用不再有效),或者很少,因为我想要释放一个智能对象。 (如果这就是意思,我宁愿调用 Release 并让维护者清楚地了解代码的含义。)正如您所注意到的,这在 C# 中几乎毫无意义。

由于不同的原因,您也经常在 VB/VBScript 中看到这种模式。我想了一下可能会导致这种情况的原因:

链接

I suspect that this pattern comes from translating C++ code to C# without pausing to understand the differences between C# finalization and C++ finalization. In C++ I often null things out in the destructor, either for debugging purposes (so that you can see in the debugger that the reference is no longer valid) or, rarely, because I want a smart object to be released. (If that's the meaning I'd rather call Release on it and make the meaning of the code crystal-clear to the maintainers.) As you note, this is pretty much senseless in C#.

You see this pattern in VB/VBScript all the time too, for different reasons. I mused a bit about what might cause that here:

Link

一口甜 2024-09-13 22:29:05

分配 null 的约定可能源于 foo 是实例变量而不是局部变量的事实,您应该在 GC 收集它之前删除引用。有人在第一句话中就睡着了,并开始取消所有变量;众人跟了上去。

May be the convention of assigning null originated from the fact that had foo been an instance variable instead of a local variable, you should remove the reference before GC can collect it. Someone slept during the first sentence and started nullifying all their variables; the crowd followed.

如梦初醒的夏天 2024-09-13 22:29:05

它来自 C/C++,其中对已释放的指针执行 free()/delete 可能会导致崩溃,而释放 NULL 指针则不会执行任何操作。

这意味着这个构造 (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;
}

结论:IT 在 C#、Java 和几乎任何其他垃圾收集语言中是完全不必要的。

It comes from C/C++ where doing a free()/delete on an already released pointer could result in a crash while releasing a NULL-pointer simply did nothing.

This means that this construct (C++) will cause problems

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

while this will work

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

Conclusion: IT's totally unneccessary in C#, Java and just about any other garbage-collecting language.

刘备忘录 2024-09-13 22:29:05

考虑稍作修改:

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);
} 

作者可能希望确保如果先前抛出并捕获异常,伟大的 Vavoom (*) 不会获得指向格式错误的对象的指针。偏执导致防御性编码,在这个行业不一定是坏事。

(*) 如果您知道他是谁,您就知道。

Consider a slight modification:

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);
} 

The author(s) may have wanted to ensure that the great Vavoom (*) did not get pointers to malformed objects if an exception was previously thrown and caught. Paranoia, resulting in defensive coding, is not necessarily a bad thing in this business.

(*) If you know who he is, you know.

故事与诗 2024-09-13 22:29:05

VB 开发人员必须处置所有对象,以尝试减少内存泄漏的可能性。我可以想象这就是 VB 开发人员迁移到 .NEt / c# 时的来源

VB developers had to dispose all of their objects, to try and mitigate the chance of a Memory leak. I can imagine this is where it has come from as VB devs migrated over to .NEt / c#

在巴黎塔顶看东京樱花 2024-09-13 22:29:05

我可以看到它要么是对垃圾收集工作原理的误解,要么是强制 GC 立即启动的努力 - 也许是因为对象 foobar 是相当大。

I can see it coming from either a misunderstanding of how the garbage collection works, or an effort to force the GC to kick in immediately - perhaps because the objects foo and bar are quite large.

爱她像谁 2024-09-13 22:29:05

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

但它可能并非源自 Java,因为将其用于静态变量以外的任何内容在 Java 中也是没有意义的。

I've seen this in some Java code before. It was used on a static variable to signal that the object should be destroyed.

It probably didn't originate from Java though, as using it for anything other than a static variable would also not make sense in Java.

孤独患者 2024-09-13 22:29:05

它来自 C++ 代码,尤其是智能指针。在这种情况下,它大致相当于 C# 中的 .Dispose()

这不是一个好的做法,最多只是开发人员的本能。在 C# 中分配 null 没有任何实际价值,除非可以帮助 GC 打破循环引用。

It comes from C++ code especially smart pointers. In that case it's rougly equivalent to a .Dispose() in C#.

It's not a good practice, at most a developer's instinct. There is no real value by assigning null in C#, except may be helping the GC to break a circular reference.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文