这种双重实例化是有害的,还是根本没有必要
本文关键字:实例化 | 更新日期: 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();
}