在仔细阅读遗留源时,我发现了这一点:
DataSet myUPC = new DataSet();
myUPC = dbconn.getDataSet(dynSQL);
Resharper 正确地将其中的“new Dataset()”部分“变灰”,并建议“删除冗余初始化程序”,但它就这么无害吗?编译器是否只是在第二次分配之前处理第一个实例?IOW,第一个任务是完全没有必要的,还是可能有害的?
在仔细阅读遗留源时,我发现了这一点:
DataSet myUPC = new DataSet();
myUPC = dbconn.getDataSet(dynSQL);
Resharper 正确地将其中的“new Dataset()”部分“变灰”,并建议“删除冗余初始化程序”,但它就这么无害吗?编译器是否只是在第二次分配之前处理第一个实例?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);
此外,Just-In-Time 编译器不能简单地优化构造函数调用,因为它可能会影响被初始化对象以外的其他事物(即具有副作用)。例如,如果编译器优化了构造函数调用,那么构造函数将不会在控制台上打印任何内容。这是不希望或不期望的,因此构造函数调用必须保留在那里并产生一个新实例。
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();
}