这种双重实例化是有害的,还是根本没有必要

本文关键字:实例化 | 更新日期: 2023-09-27 18:00:58

在仔细阅读遗留源代码时,我发现了以下内容:

DataSet myUPC = new DataSet();
myUPC = dbconn.getDataSet(dynSQL);

Resharper正确地"灰显"了其中的"new Dataset(("部分,并建议"删除多余的initalizer",但它也像那样无害吗?编译器是否只是在第二次赋值之前处理第一个实例?IOW,第一个任务是完全没有必要的,还是潜在的危害?

这种双重实例化是有害的,还是根本没有必要

编译器是否只是在第二次赋值之前处理第一个实例?

不,这里没有自动处理。

IOW,第一个任务是完全没有必要的,还是潜在的危害?

它在两个小方面是有害的:

  • 它为初始化代码和垃圾收集器都做了更多的工作。这不太可能意义重大,但它确实存在。如果构造函数获取了一些本机资源,则可能会更严重
  • 它让你的代码看起来像是想做一些它实际上不想做的事情。你不想创建一个新的空DataSet,为什么要这样做

只需使用您真正想要的值初始化变量:

DataSet myUPC = dbconn.getDataSet(dynSQL);

现在,您的代码准确地显示了您想要做的事情。(请注意,我会修改方法名称,使其遵循.NET命名约定。(

这通常是不必要的。

如果DataSet构造函数启动了一些长时间运行的后台线程,或者分配了大量内存,这些内存将一直保留到冗余对象被垃圾收集(这不是即时的(,那么这将是非常有害的。

然而,一个有礼貌的构造函数不应该做这些事情,所以你可能是安全的。然而,每当我看到这一点时,我都会注意并修复代码,因为正如Jon Skeet所指出的,这会让你的代码做不必要的工作——创建和处理一个你无意使用的对象,看起来你错过了一些代码。

IOW,第一个任务是完全没有必要的,还是潜在的危害?

第一个赋值是不必要的,但也可能有害,具体取决于类型。第一个实例将成为GC的合格实例,但仍然会被初始化(毫无理由(并且从未使用过。

由于没有其他引用,它会一直挂在垃圾收集之前。但是,如果构造函数有副作用(可能DataSet的构造函数没有(,它也可能是有害的。

myUPC被dbconn.getDataSet((的输出覆盖。这是因为getDataSet是一个工厂方法,并返回Dataset类型的对象。

是的,正如其他答案所示,它实际上有点有害,主要是因为它分配了一个从未使用过的对象,最终必须进行垃圾收集。但让我更详细一点。

第一个实例可以被处理吗

  DataSet myUPC = new DataSet();
  myUPC = dbconn.getDataSet(dynSQL);

编译器是否只是在第二次赋值之前处理第一个实例?

假设您的意思是丢弃收集新的未使用实例的GC(垃圾收集器(,那么答案是:。让我详细说明一下:

GC可以随时运行,例如,当堆很快就会满时,或者当试图分配一个不适合堆剩余空间的对象时。因此,GC也可能(只是偶然(正好在您的第一个语句和第二个语句之间运行。但是,这不会收集new DataSet()对象,因为在局部变量myUPC中有对它的引用。只有当没有对对象的引用时,对象才被视为集合1

1(实际上,只有当从所谓的到对象没有引用链时,才考虑收集对象。根包括静态字段、方法参数、局部变量和求值堆栈。

构造函数调用是否可以被优化掉

DataSet myUPC; /* Optimized away? */
myUPC = dbconn.getDataSet(dynSQL);

此外,实时编译器不能简单地优化构造函数调用,因为它可能会影响初始化对象之外的其他事情(即有副作用(。例如,如果编译器优化了构造函数调用,那么构造函数将不会在控制台上打印任何内容。这是不希望或不期望的,因此构造函数调用必须留在那里并产生一个新实例。

class MyClass
{
    public MyClass()
    {
        Console.WriteLine("Constructor called!");
    }
}
abstract class X
{
    void Do()
    {
        MyClass my = new MyClass();  // Should always print "Constructor called!"
        my = GetMyClass();
        // ...
    }
    protected abstract MyClass GetMyClass();
}